norritt
norritt

Reputation: 355

Is it really impossible to skip template parameters with default arguments in C++, why does syntax suggest otherwise?

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?

Upvotes: 4

Views: 1307

Answers (2)

Quuxplusone
Quuxplusone

Reputation: 27065

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 {};

Upvotes: 0

Kerrek SB
Kerrek SB

Reputation: 476980

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]

Upvotes: 3

Related Questions