4

Consider the following code which uses "template template" parameters to instantiate a class template using multiple types:

#include <iostream>
using namespace std;

enum E
{
    a = 0,
    b = 1
};

template <template <E> class Action, class T>
void do_something(const T& value)
{
    typedef Action<a> type1;
    typedef Action<b> type2;
}

template <E e, class Enable = void>
class Foo
{

};

int main()
{
    do_something<Foo>(int(55));
}

Using an older compiler (GCC 4.1.2), the above code compiles fine. However, using a newer compiler (GCC 4.4.6 or 4.8.1), the following error is produced:

test3.cpp:25:27: error: no matching function for call to ‘do_something(int)’
  do_something<Foo>(int(55));

So it looks like GCC can't bind to do_something, because the template template parameters only declare a single parameter (an Enum), but Foo actually takes two template parameters (even though one is default.) I guess GCC 4.1.2 allowed the default parameter to be ignored.

Okay, so if I change the template definition to:

template <template <E, class> class Action, class T>
void do_something(const T& value)
{
    typedef Action<a> type1;
    typedef Action<b> type2;
}

...then no version of GCC I tested will compile it. They all produce a similar error:

test3.cpp:13: error: wrong number of template arguments (1, should be 2)
test3.cpp:10: error: provided for ‘template<E <anonymous>, class> class Action’

So now, the compiler complains because the expression typedef Action<a> type1 only provides a single template parameter. Apparently, I'm not able to implicitly use the default parameter here.

Is there some way I can use the default parameter of a template in a template template function?

6
  • 1
    In your final definition of do_someting, could you replace the first class with class=void? This might tell it that Action is a two-parameter template with one default. I think I don't want this to work, because it means the default is specified twice! Commented Oct 15, 2013 at 20:29
  • That actually worked. Is that allowed by the standard? Commented Oct 15, 2013 at 20:31
  • It was a wild guess on my part! No idea about the standard. I'm on g++-4.6.3 and it works for me. Commented Oct 15, 2013 at 20:42
  • @AaronMcDaid [temp.param]/14 "A template-parameter of a template template-parameter is permitted to have a default template-argument." However, that does not affect whether a template is a valid template template-argument (it's not really useful, live example). Commented Oct 15, 2013 at 20:49
  • In my experiments, on g++-4.6.3, any default specified in do_something takes priority over any default specified in Foo. This is all a little weird and I don't like it even if it is standard. Is there a tidier way to make a single-parameter template which is an alias to a two-parameter-with-one-default template. AFAIK, the new using template-aliases in C++11 might be relevant. Commented Oct 15, 2013 at 21:04

2 Answers 2

6

Default arguments are ignored for parameters of template arguments. There's this example in n3337, chapter [temp.arg.template], paragraph 2:

template<class T> class A { /∗ ... ∗/ };
template<class T, class U = T> class B { /∗ ... ∗/ };
template <class ... Types> class C { /∗ ... ∗/ };
template<template<class> class P> class X { /∗ ... ∗/ };
template<template<class ...> class Q> class Y { /∗ ... ∗/ };
X<A> xa; // OK
X<B> xb; // ill-formed: default arguments for the parameters of a template argument are ignored
X<C> xc; // ill-formed: a template parameter pack does not match a template parameter
Y<A> ya; // OK
Y<B> yb; // OK
Y<C> yc; // OK

Note the comment at X<B> xb; above. I can't find the normative text, I'm afraid.

You can correlate this with functions - default arguments are not a part of a signature, either. The same thing would also happen if you tried to call a function that has a parameter defaulted through a function pointer.

Sign up to request clarification or add additional context in comments.

2 Comments

Yet apparently you can put default arguments in template template parameters
Yes, apparently - but that's a different thing than what that comment is saying.
1

With a using template alias, a new feature in C++11, you can create a one-parameter template that is equivalent to another template that has two parameters, one of which is defaulted.

template <E e> using Foo1 = Foo<e>;

This creates Foo1, a one-parameter template, even though Foo technically has two arguments, one of which is defaulted. You can use it as:

do_something<Foo1>(int(55));

Alternatively, if C++11 features such as using are not available, then you scan specify the default in your declaration of do_something. This means then, unfortunately, that do_something can no longer deal with simple one-arg templates. Hence, I think the using method above is better.

template <template <E, class = void> class Action, class T>
void do_something(const T& value);

If you take this approach, putting the default in the args to do_something, then this default takes precedence over the default specified at the declaration Foo. This is based on my experiments, and I can't comment with confidence on what is, and is not, standard. But I do think the using trick is fully standards-compliant regarding C++11.

(Ubuntu clang version 3.0-6ubuntu3)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.