#include <deque>
template <typename T>
class MyVector {
public:
void push_back(T const&) {}
};
template <typename T, template <typename> class CONT = /*MyVector*/
std::deque >
class Stack {
private:
CONT<T> elems; // elements
public:
void push(T const&); // push element
};
template <typename T, template <typename> class CONT>
void Stack<T,CONT>::push(T const& elem)
{
elems.push_back(elem); // append copy of passed elem
}
int main() {
Stack<int> iStack;
iStack.push(1);
return 0;
}
With Visual Studio 2005 I get the following error message:
error C3201: the template parameter list for class template
'std::deque' does not match the template parameter list for template
parameter 'CONT'
error C3201: the template parameter list for class template
'std::deque' does not match the template parameter list for template
parameter 'CONT'
error C2976: 'std::deque' : too few template arguments
deque(486) : see declaration of 'std::deque'
The problems is not the template template parameter itself.
When I substitute the default argument with "MyVector" the
code compiles fine.
How do I have to modify the sample to make it work with the
original STL containers std::deque, std::vector, ...
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
The error messages actually tells you what the problem is:
The problem is that std::deque and std::vector also has additional
default template arguments. You require a template with a single
argument, so the standard containters do not match.
Bo Persson
std::deque takes three template parameters, not one.
Well, you seem to be using a good compiler - it told you what to do
right there! Indeed, just look at the declaration of std::deque:
template <typename T, typename Allocator = std::allocator<T> >
class deque { ... };
As you can see, it has two template arguments, and not one. True, the
second one has a default value, so you mostly only care about the
first argument - but it's not good enough in this case.
The obvious fix is to change your template correspondingly, probably
something along the lines of:
template <typename T, template <typename, typename> class CONT =
std::deque, typename Allocator>
class Stack { ... };
And, of course, consider using a C++ book that's more up-to-date with
respect to the C++ standard.
You must know that std::deque is also a template,but your template
Stack's second argument does not make deque defined correctly,
so maybe you can try this:
template <typename T, template <typename> class CONT = /*MyVector*/
std::deque<T> >
This is because std::deque is a class template that takes more than
one template parameter "typename T". It also takes an allocator
"class Alloc".
For reasons I'm not aware of the C++ standard says that you can't pass
such a class template -- even with a default allocator type -- as a
template template parameter that only takes one template parameter.
This is probably one reason why template template parameters are not
so popular. In future C++ you will be able to do a "template typedef"
template<typename T>
using vector_with_standard_alloc = std::vector<T>;
so you can use "vector_with_standard_alloc" as default template
argument. Another workaround is to define your own container class
template with only one template parameter (like MyVector).
Cheers!
SG
std::deque has *two* template parameters. Therefore it doesn't match
your template template class parameter.
> The code sample below is not working. It is taken from
> Nicolai Josuttis "C++ Templates: The Complete Guide".
> In the chapter 5.4 Template Template Parameters it reads
> [slightly stripped]:
>
> #include <deque>
>
> template <typename T>
> class MyVector {
> public:
> void push_back(T const&) {}
> };
>
> template <typename T, template <typename> class CONT = /*MyVector*/> std::deque >
<snip>
Others have explained the problem; I can offer some alternative
solutions:
1. Metafunction Class:
// Create one of these for each container type
struct make_deque
{
template <class T>
struct apply
{
typedef std::deque<T> type;
};
};
template <typename T, typename GenCont = make_deque>
class Stack {
private:
typename GenCont<T>::type elems; // elements
public:
void push(T const&); // push element
};
2. MPL Lambda Expression:
#include <boost/mpl/apply.hpp>
#include <boost/mpl/placeholders.hpp>
namespace mpl = boost::mpl;
template <typename T, typename GenCont = std::deque<mpl::_> >
class Stack {
private:
typename mpl::apply<GenCont,T>::type elems;
public:
void push(T const&);
};
See http://www.boost.org/libs/mpl and http://www.boostpro.com/mplbook
for more info.
Regards,
--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com
You should have kept reading the book. Because he explains everything
about this problem and gives a solution. Quote:
If you try to use the new version of Stack, you get an error message
saying that the default value std::deque is not compatible with the
template template parameter CONT. The problem is that a template
template argument must be a template with parameters that exactly
match the parameters of the template template parameter it
substitutes. Default template arguments of template template arguments
are not considered, so that a match cannot be achieved by leaving out
arguments that have default values.
The problem in this example is that the std::deque template of the
standard library has more than one parameter: The second parameter
(which describes a so-called allocator) has a default value, but this
is not considered when matching std::deque to the CONT parameter.
There is a workaround, however. We can rewrite the class declaration
so that the CONT parameter expects containers with two template
parameters:
template <typename T,
template <typename ELEM,
typename ALLOC = std::allocator<ELEM> >
class CONT = std::deque>
class Stack {
private:
CONT<T> elems; // elements
…
};
typename GenCont::template apply<T>::type elems;
You forgot the extra level of indirection ;)
--
Giovanni P. Deretta
--