Reputation: 355
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
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);
}
X
is deduced as double
(and the default, if any, would go unused).Y
cannot be deduced in this call, so the default of int
is used.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
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