3

From this page on cppreference about default template arguments:

If the default is specified for a template parameter of a primary class template, [...] each subsequent template parameter must have a default argument, except the very last one may be a template parameter pack (since C++11). In a function template, there are no restrictions on the parameters that follow a default [...].

This means the following code compiles:

template <typename U = int, typename T>
U foo(T) { /*... */ }

while this doesn't:

template <typename U = int, typename T>
struct S {
    S(T) { /* ... */ }
};

What's the reasoning behind this?

This kind of makes sense to me until C++17, as you had to specify T (hence U) when constructing an instance of S:

S<int, double> s(1.2);

However, C++17 introduced CTAD: couldn't the standard allow non defaulted template parameters after the last defaulted one as long as they're deducible from the initializer?

Note: I've already had a look at question "Non-last default template arguments for function templates" but it doesn't answer mine.

Elliott
  • 2,603
  • 2
  • 18
  • 35
paolo
  • 2,345
  • 1
  • 3
  • 17
  • `What's the reasoning behind this?` This seems like an open-ended question, so might be frowned upon here. But honestly, I like it! =) – Elliott Jul 21 '22 at 10:13
  • _"couldn't the standard ..."_ but it didn't. And that's the extent of answers you can get. We can't read the committee's mind. – Passer By Jul 21 '22 at 10:13
  • @PasserBy I'd hope for some answer like "If we allowed that, then _X_ would not work" or so. I mean, my assumption is there's a motivation behind this choice. – paolo Jul 21 '22 at 10:15
  • @paolo It's almost guaranteed there is some way to twist/bloat the rules until you get what you want, but it's not worth the effort. Almost always, the question is _why_ instead of _why not_. – Passer By Jul 21 '22 at 12:23
  • CTAD from partially-specified template arg lists was debated twice (original paper, and the "Holes" paper) and rejected/postponed both times. [r0 of the Holes paper](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1021r0.html) mentions the ambiguity with partial lists – Cubbi Jul 21 '22 at 17:10

1 Answers1

0

There's an important restriction with CTAD that you must deduce all of the template arguments. If you specify any template arguments explicitly, then you will not get CTAD. This means that default template arguments and CTAD aren't really helpful to one another--at any given place you are may be using one or the other but not both.

Hence, the introduction of CTAD doesn't change anything about the reason why you can't have parameters without default arguments following parameters with default arguments in a template class: there's no way to specify the later parameters if you are taking advantage of the default arguments, and since you have to specify those parameters to use the template, the default arguments are useless.

In your concrete example, suppose the language allowed:

template <typename U = int, typename T>
struct S {
    S(T) { /* ... */ }
};

There would be no way to use this template without specifying either both U and T, or (if you have an explicit deduction guide) neither. So there's no example where you could ever make use of the default int argument for U, which means specifying it would likely be a bug, so it's better just to make it illegal.

Note how this restriction doesn't apply to function templates, where you might be able to deduce later template parameters from the arguments passed to the function.

user3188445
  • 4,062
  • 16
  • 26