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?

Channel72
  • 24,139
  • 32
  • 108
  • 180
  • 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! – Aaron McDaid Oct 15 '13 at 20:29
  • That actually worked. Is that allowed by the standard? – Channel72 Oct 15 '13 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. – Aaron McDaid Oct 15 '13 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](http://coliru.stacked-crooked.com/a/ed1d0a3a06a7255b)). – dyp Oct 15 '13 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. – Aaron McDaid Oct 15 '13 at 21:04
  • @Dyp, I agree it's not too useful. Ideally we'd want a template template that can take, as its argument, any template that has exactly one non-default parameter. Then we could do `C` or `C` in your live example. But, as you have demonstrated, if `C` 'allows' a default for the second parameter, then it requires a two-parameter template. – Aaron McDaid Oct 15 '13 at 21:06

2 Answers2

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.

jrok
  • 54,456
  • 9
  • 109
  • 141
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)

Aaron McDaid
  • 26,501
  • 9
  • 66
  • 88