Topic: Functions templated on containers


Author: herbs@cntc.com (Herb Sutter)
Date: 1997/12/18
Raw View
It just occurred to me that it would be nice to be able to write something
like this (warning -- seat-of-the-pants example follows):

    // Function must take iterators which, when dereferenced,
    // yield (references to or temporary) Employee objects, or
    // possibly Employee-derived objects, and not some other type.
    //
    template<typename T>
    Money TotalSalary(
        T<Employee>::iterator first,
        T<Employee>::iterator last
        )
    {
        Money total(0);
        while( first != last )
        {
            // Here's why *first must yield an Employee object.
            total += (*first).AnnualSalary();
            ++first;
        }
        return total;
    }

The reason is that sometimes I may want to store Employee objects in a
vector, sometimes in a list, etc., and still use the same function
regardless of the type of container being used. Is this kind of usage
intended to be supported?

Herb


PS: I realize that I'm out of luck if the Employee objects are in a map
since *first yields a pair, but that's ok since then it wouldn't match the
above signature. If this idiom is even allowed, I would then (try to)
provide another template:

    template<typename T1, typename T2>
    Money TotalSalary(
        T1<T2,Employee>::iterator first,
        T1<T2,Employee>::iterator last
        );
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: Pete Becker <petebecker@acm.org>
Date: 1997/12/19
Raw View
Herb Sutter wrote:
>
> It just occurred to me that it would be nice to be able to write something
> like this (warning -- seat-of-the-pants example follows):
>
>     // Function must take iterators which, when dereferenced,
>     // yield (references to or temporary) Employee objects, or
>     // possibly Employee-derived objects, and not some other type.
>     //
>     template<typename T>
>     Money TotalSalary(
>         T<Employee>::iterator first,
>         T<Employee>::iterator last
>         )
>     {
>         Money total(0);
>         while( first != last )
>         {
>             // Here's why *first must yield an Employee object.
>             total += (*first).AnnualSalary();
>             ++first;
>         }
>         return total;
>     }
>
> The reason is that sometimes I may want to store Employee objects in a
> vector, sometimes in a list, etc., and still use the same function
> regardless of the type of container being used. Is this kind of usage
> intended to be supported?

I'm not sure what you're getting at. Any sequence container that holds
Employee objects has iterators that point to Employees, so you don't
need to insist on getting an iterator from a container that holds
Employees. Just do it:

     template<typename T>
     Money TotalSalary( T first, T last )
     {
         Money total(0);
         while( first != last )
         {
             // Here's why *first must yield an Employee object.
             total += (*first).AnnualSalary();
             ++first;
         }
         return total;
     }

       If *first doesn't yield an Employee object this won't compile. (more
accuratly, if *first yields a type that doesn't have an accessible
member function named AnnualSalary with an appropriate signature it
won't compile).
       In fact, with just a bit of a tweak, this algorithm is already part of
the standard (quick and dirty example, not tested):

template<class Iterator>
struct add_salary
{
       Money operator()(Money prev, Iterator it)
               {
               return prev + (*it).AnnualSalary();
               }
};

typedef vector<Employee> Employees;
Employees emps;

Money total_salary =
       std::accumulate( emps.begin(), emps.end(),
       Money(0), add_salary<Employees::iterator>() );
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: David Wragg <dpw@doc.ic.ac.uk>
Date: 1997/12/22
Raw View
herbs@cntc.com (Herb Sutter) writes:
> It just occurred to me that it would be nice to be able to write something
> like this (warning -- seat-of-the-pants example follows):
>
>     // Function must take iterators which, when dereferenced,
>     // yield (references to or temporary) Employee objects, or
>     // possibly Employee-derived objects, and not some other type.
>     //
>     template<typename T>
>     Money TotalSalary(
>         T<Employee>::iterator first,
>         T<Employee>::iterator last
>         )
>     {
>          ...
>     }
>

The nearly-standard allows exactly what you want. The actual syntax is:

template<template<class> class T>
Money TotalSalary(
    T<Employee>::iterator first,
    T<Employee>::iterator last
    )
{
     ...
}

So now you can do higher-order programming in C++!
Dave Wragg.
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: Valentin Bonnard <bonnardv@pratique.fr>
Date: 1997/12/22
Raw View
Herb Sutter <herbs@cntc.com> writes:

> It just occurred to me that it would be nice to be able to write something
> like this (warning -- seat-of-the-pants example follows):
>
>     // Function must take iterators which, when dereferenced,
>     // yield (references to or temporary) Employee objects, or
>     // possibly Employee-derived objects, and not some other type.
>     //
>     template<typename T>
>     Money TotalSalary(
>         T<Employee>::iterator first,
>         T<Employee>::iterator last
>         )

> The reason is that sometimes I may want to store Employee objects in a
> vector, sometimes in a list, etc., and still use the same function
> regardless of the type of container being used. Is this kind of usage
> intended to be supported?

I think you are looking for template template arguments, which
exist, but have rarely been implemented (I think only one
compiler has them):

    template <template T<typename> > // not sure about the syntax
    Money TotalSalary(
        T<Employee>::iterator first,
        T<Employee>::iterator last
        )
    {
        Money total(0);
        while( first != last )
        {
            // Here's why *first must yield an Employee object.
            total += (*first).AnnualSalary();
            ++first;
        }
        return total;
    }

This kind of second order genericity can often be simulated/
replaced by other tools, in particular template members.

However, vector takes two type template arguments, so it
isn't clear that the above will work with vector, list or
any STL containner.

Today, you can write:

    template <class InputIterator>
    Money TotalSalary (InputIterator first, InputIterator last);

And I don't see any strong reason to restrict the iterator
used.

--

Valentin Bonnard                mailto:bonnardv@pratique.fr
info about C++/a propos du C++: http://www.pratique.fr/~bonnardv/
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: kanze@gabi-soft.fr (J. Kanze)
Date: 1997/12/22
Raw View
herbs@cntc.com (Herb Sutter) writes:

|>  It just occurred to me that it would be nice to be able to write something
|>  like this (warning -- seat-of-the-pants example follows):
|>
|>      // Function must take iterators which, when dereferenced,
|>      // yield (references to or temporary) Employee objects, or
|>      // possibly Employee-derived objects, and not some other type.
|>      //
|>      template<typename T>
|>      Money TotalSalary(
|>          T<Employee>::iterator first,
|>          T<Employee>::iterator last
|>          )
|>      {
|>          Money total(0);
|>          while( first != last )
|>          {
|>              // Here's why *first must yield an Employee object.
|>              total += (*first).AnnualSalary();
|>              ++first;
|>          }
|>          return total;
|>      }
|>
|>  The reason is that sometimes I may want to store Employee objects in a
|>  vector, sometimes in a list, etc., and still use the same function
|>  regardless of the type of container being used. Is this kind of usage
|>  intended to be supported?

Why "(*first).annualSalary()", and not "first->annualSalary()"?  Even if
*first yielded some sort of helper class, one could imagine that the
iterator would provide an operator-> which would work.  (On the other
hand, I don't think that the standard requires it.  In 24.1, i->m is
always discussed in terms of (*i).m, and always conditioned by the fact
that (*i).m is well formed.)

--
James Kanze    +33 (0)1 39 23 84 71    mailto: kanze@gabi-soft.fr
GABI Software, 22 rue Jacques-Lemercier, 78000 Versailles, France
Conseils en informatique orient   e objet --
              -- Beratung in objektorientierter Datenverarbeitung
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: herbs@cntc.com (Herb Sutter)
Date: 1997/12/22
Raw View
David Wragg <dpw@doc.ic.ac.uk> wrote:
>herbs@cntc.com (Herb Sutter) writes:
>>     template<typename T>
>>     Money TotalSalary(
>>         T<Employee>::iterator first,
>>         T<Employee>::iterator last
>>         )

>The nearly-standard allows exactly what you want. The actual syntax is:
>
>template<template<class> class T>
>Money TotalSalary(
>    T<Employee>::iterator first,
>    T<Employee>::iterator last
>    )

Thanks, Dave. I never got into the details of template template
parameters. Now all I need is a compiler that supports them. :-)
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: "Bradd W. Szonye" <bradds@concentric.net>
Date: 1997/12/23
Raw View
J. Kanze wrote in message ...

>Why "(*first).annualSalary()", and not "first->annualSalary()"?

Habit? I'm accustomed to working with compilers that choke on operator-> for
basic types, even if it's never used. The (*ptr).member syntax will work
with these broken compilers/libraries, where the ptr->member syntax might
not.

The version of HP CC I'm currently using seems to still have this problem,
although it's gotten noticeably better about some other things that used to
bother me. (Speaking of which: if anyone out there has guru-level advice on
getting the HP CC template mechanisms to work "sensibly" and the patience to
explain them to me, please leave me e-mail! I've been beating it about the
head and shoulders to implement a small portion of the standard library, but
it gets upset about things like static data members and
templates-of-templates [ie. vector<string>].)
---
Bradd W. Szonye
bradds@concentric.net
http://www.concentric.net/~Bradds
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: herbs@cntc.com (Herb Sutter)
Date: 1997/12/24
Raw View
kanze@gabi-soft.fr (J. Kanze) wrote:
>Why "(*first).annualSalary()", and not "first->annualSalary()"?

Unconscious habit, from the times before iterator::operator->().
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Alexandre Oliva <oliva@dcc.unicamp.br>
Date: 1997/12/24
Raw View
David Wragg <dpw@doc.ic.ac.uk> wrote:

> The nearly-standard allows exactly what you want. The actual syntax is:

> template<template<class> class T>
> Money TotalSalary(
> T<Employee>::iterator first,
> T<Employee>::iterator last
> )

Isn't typename required in this case?

//...
  typename T<Employee>::iterator first,
//...

--
Alexandre Oliva
mailto:oliva@dcc.unicamp.br mailto:aoliva@acm.org
http://www.dcc.unicamp.br/~oliva
Universidade Estadual de Campinas, SP, Brasil
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]