4

I've been trying to find a way to skip a template parameter not located at the end of the template parameter list, in a derived class that has been assigned a default in its base class.

I've done some research on this topic, also here on SO. While similar questions have been discussed on SO - many answers basically suggesting it doesn't work were related to very special cases like the hash map case here. Also I found this answer by "Potatoswatter", which in my opinion contradicts the impossibility of skipping such a parameter. In his answer he claims this declaration would be valid:

template< class A, class B = int, class C >
class X;

Assuming it is true that a template parameter may not be skipped (unless at the end of the argument list) such a declaration would make no sense at all. Since B is assigned a default value, but followed by C which has no default, in this case value B would always have to be assigned explicitly, rendering the assignment of int as default for B completely useless. The only scenario where the declaration of X above would make sense is one where one of the following declarations of Y would be valid:

class Y : public X<double, , const std::string&> { ... }

class Y : public X<A = double, C = const std::string&> { ... }

So is it really impossible to skip a template parameter that is not located at the end of the template parameter list when deriving a specialized class?

If it is impossible why so, and why does legal syntax apparently suggest otherwise (see class X example above)?

If it is in fact not impossible, how can one skip a template argument that has been assigned a default?

Community
  • 1
  • 1
norritt
  • 345
  • 4
  • 16
  • 2
    I guess `class A, class B = int, class C` is not valid unless in the same translation unit you do have a declaration where `C` *has* a default value – Piotr Skotnicki Sep 05 '15 at 15:44
  • 1
    Nice idea, didn't think of that yet, this would be a very special case where the declaration would make sense, even though skipping is not valid. – norritt Sep 05 '15 at 15:56

2 Answers2

3

The relevant standardese is contained in "Template Parameters [temp.param]" (14.1).

Essentially, a default argument may only be used if the parameter to which it applies is not followed by any non-pack parameters that do not have default arguments ([temp.param]/11). However, the syntax you quoted is usable in a declaration in the situation described by [temp.param]/10:

The set of default template-arguments available for use is obtained by merging the default arguments from all prior declarations of the template in the same way default function arguments are (8.3.6). [Example:

template<class T1, class T2 = int> class A;
template<class T1 = int, class T2> class A;

is equivalent to

template<class T1 = int, class T2 = int> class A;

end example]

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • Can you think of any example, except the derivation scenario Piotr mentioned in his comment above, where such a fragmented template declaration would make sense? – norritt Sep 05 '15 at 15:59
  • 2
    @norritt: No. Not everything that you *can* do with C++ is something you *should* do. – Kerrek SB Sep 05 '15 at 16:03
0

The word "class" is kind of hidden in your question, so I thought I'd mention (even though this isn't exactly an answer) that template parameters with defaults can be "skipped", sort of, in calls to function templates. Consider this code:

template<class X, class Y = int, class Z>
void foo(X x, Y y, Z z) {
    (void)x, (void)y, (void)z;
    puts(__PRETTY_FUNCTION__);
}

int main()
{
    foo(3.14, {}, 1.45f);
}
  • Template parameter X is deduced as double (and the default, if any, would go unused).
  • Template parameter Y cannot be deduced in this call, so the default of int is used.
  • Template parameter Z is deduced as float.

Providing defaults for function template parameters happens a lot in the STL these days because of the undeduceability of {} in a function argument list. See for example std::exchange or constructor #8 of std::optional.

You might expect that a similar trick could be used with constructor template argument deduction in C++17, but my experimentation suggests that this is not the case. In class template definitions, the compiler will produce a diagnostic if you put a template-parameter-with-default earlier in the list than a template-parameter-without-default.

template<class X, class Y = int, class Z>  // error!
struct Foo {};
Quuxplusone
  • 23,928
  • 8
  • 94
  • 159