Re: Keywords 'class' and 'typename' not interchangeable with template template parameters?
"Richard Smith" <richard@ex-parrot.com> schrieb im Newsbeitrag news:4871b34a-20e9-47d3-89b8-fc3d1cfcdf93@v31g2000vbs.googlegroups.com...
On Apr 19, 9:41 pm, "Matthias Hofmann" <hofm...@anvil-soft.com> wrote:
I just stumbled into the miraculous world of template template parameters
and wrote a short piece of testing code to play around with this rather
new
C++ feature:
I'm curious as to why template template parameters were added to the
language in the first place.
So was this -- allowing the choice of container as a point of
customisation -- their main anticipated use? Or were other major uses
expected?
When I first learned about template template parameters a couple of days
ago, I was wondering myself what they could be used for. Some people seem
use them for policy based code design, something that I am not familiar
with, although I would like to know what that is. But today I came up with a
very useful application of template template parameters with regards to a
base class I wrote long ago. It provides a virtual copy constructor to all
classes deriving from it:
#include <memory>
#include <typeinfo>
#include <stdexcept>
// Provides a virtual copy constructor.
template <class T> class Cloneable
{
// A leaf class must implement this
// function to allocate a copy of the
// object and return a pointer to it.
virtual T* clone() const = 0;
public:
std::auto_ptr<T> Clone() const
{
std::auto_ptr<T> temp( clone() );
if ( typeid( *temp ) != typeid( *this ) )
throw std::logic_error( "type mismatch" );
return temp;
}
virtual ~Cloneable() {};
};
// Example of derived class.
class Object : public Cloneable<Object>
{
virtual Object* clone() const
{ return new Object( *this ); }
};
// Example of usage.
int main()
{
std::auto_ptr<Object> p( new Object );
std::auto_ptr<Object> p2 = p->Clone();
return 0;
}
I like this class very much, it uses the non-virtual interface idiom I
learned about in an article by Herb Sutter at the following URL:
http://www.gotw.ca/publications/mill18.htm
The only thing that has been bothering me a little is that it only works
with std::auto_ptr. It would have to be completely rewritten in order to
work with boost::shared_ptr, for example. Fortunately, template template
parameters offer a solution to fix this shortcoming:
// Provides a virtual copy constructor.
template <class T, template <typename> class
smart_ptr = std::auto_ptr> class Cloneable
{
// A leaf class must implement this
// function to allocate a copy of the
// object and return a pointer to it.
virtual T* clone() const = 0;
public:
smart_ptr<T> Clone() const
{
smart_ptr<T> temp( clone() );
if ( typeid( *temp ) != typeid( *this ) )
throw std::logic_error( "type mismatch" );
return temp;
}
virtual ~Cloneable() {};
};
// Example of derived class, now using
// boost::shared_ptr instead of std::auto_ptr.
class Object : public Cloneable<Object, boost::shared_ptr>
{
virtual Object* clone() const
{ return new Object( *this ); }
};
// Example of usage.
int main()
{
boost::shared_ptr<Object> p( new Object );
boost::shared_ptr<Object> p2 = p->Clone();
return 0;
}
Now isn't this neat? You can now use any smart pointer by just specifying a
second template argument when deriving from the Cloneable class, or you can
just omit the second argument in order to use std::auto_ptr, so the change
does not even break existing code, which is not very important for me as I
have never used the Cloneable class in practice.
The only thing I am a little unsecure about is the choice of the name
"smart_ptr" for the second template parameter. What happens if some library
I am using decides to define a smart pointer named "smart_ptr"? Could this
cause any naming conflicts, and if so, what can I do to prevent this?
I notice that, although fix proposed by Vandevoorde & Josuttis hasn't
been adopted in C++0x, there have been some changes to that part of
the text to incorporate parameter packs. I'm not entirely sure
whether this has fixed the problems. I can now write
template <class T, template <class...> class C>
struct X {};
X<int, std::vector> x1;
and it will compile. It's not clear to me whether std::vector's
default second argument propagates through the parameter pack,
though. I.e. can I write:
template <class T, template <class...> class C>
struct X {
C<T> data;
};
X<int, std::vector> x1;
and will x1.data be a std::vector<int, std::allocator<int>>? Playing
with gcc 4.6 suggests this works, but I've not managed to locate the
relevant text in the standard that guarantees this. Indeed, the text
in 14.1 [temp.param], paragraph 14 suggests that perhaps this example
should not compile.
Can anyone shed any light on this?
My assumption is that if you write this
X<int, std::vector> x1;
you will indeed get a std::vector<int, std::allocator<int>>, but I don't
know what you would have to do in order to get a std::vector<int,
std::allocator<double>>, for example. Maybe you would have to write
template <class T, template <class...> class C>
struct X {
C<T, std::allocator<double>> data;
};
It seems to me that if template template parameters have an important
use, it is probably in straightforward cases like these. And given
that template template parameters haven't been removed from C++0x, I'm
hopeful they've been fixed.
I don't have the impression that template template parameters are broken.
You only have to treat templates with default arguments as if these default
arguments wouldn't exist. So for the time being, you have to write
template <class T, template <class, class> class C>
struct X {
C<T, std::allocator<T>> data;
};
X<int, std::vector> x1;
You basically have to repeat the parameter list of the template template
parameter in the definition of your class, which is a little cumbersome of
course.
--
Matthias Hofmann
Anvil-Soft, CEO
http://www.anvil-soft.com - The Creators of Toilet Tycoon
http://www.anvil-soft.de - Die Macher des Klomanagers
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]