cplusplus / CWG

Core Working Group
23 stars 7 forks source link

CWG2769 [temp.deduct.general] Substitution into template parameters and default template arguments should be interleaved #363

Open zygoloid opened 1 year ago

zygoloid commented 1 year ago

Full name of submitter (unless configured in github; will be published with the issue):

Reference (section label): [temp.deduct.general]

Link to reflector thread (if any): https://lists.isocpp.org/core/2023/07/14525.php

Issue description:

[temp.deduct.genera]/5 says:

If a template argument has not been deduced and its corresponding template parameter has a default argument, the template argument is determined by substituting the template arguments determined for preceding template parameters into the default argument. [...] When all template arguments have been deduced or obtained from default template arguments, all uses of template parameters in the template parameter list of the template are replaced with the corresponding deduced or default argument values.

... which doesn't make sense. We need to have already substituted into the template parameter declaration in order to finish forming a template argument, and we need to finish forming a template argument before we can substitute it into a later default template argument. Eg, in:

struct X { constexpr operator int() { return 0; } };
template<const int*> struct Y {};
extern int arr[];
template<typename T, T K = X(), const int *p = &arr[K], Y<p> y = {}> struct A {};
A<int> a;

... we need to substitute T = int into the type of K, then convert the default template argument X() to int, then substitute the converted value of K into the default template argument of p, then substitute the converted value of p into the default argument for y. Note that the substitution into template parameters and into default template arguments is necessarily interleaved.

Suggested resolution:

Change in [temp.deduct.general]/5:

The resulting substituted and adjusted function type is used as the type of the function template for template argument deduction. For each template parameter in turn:

  • If a template argument has not been deduced and the template parameter is a parameter pack, the template argument is an empty pack.
  • Otherwise, if a template argument has not been deduced and its corresponding template parameter has a default argument, the template argument is determined by substituting the template arguments determined for preceding template parameters into the default template argument. If the template parameter does not have a default template argument, or if the substitution results in an invalid type, as described above, type deduction fails. [ Example ... ]
  • When all template arguments have been deduced or obtained from default template arguments, all uses of template parameters in the template parameter list of the template are replaced with the corresponding deduced or default argument values. The substituted template parameter is determined by substituting the template arguments determined for preceding template parameters into the template parameter. If the substitution results in an invalid type, as described above, type deduction fails.
  • The template argument is matched against the substituted template parameter ([temp.arg.general]). If the template argument does not match the substituted template parameter ([temp.arg.general]), type deduction fails. [ Note: Matching a template argument to a non-type template parameter may perform a conversion. The converted value is used for later substitutions. ]

If the function template has associated constraints ([temp.constr.decl]), those constraints are checked for satisfaction ([temp.constr.constr]). [...]

jensmaurer commented 1 year ago

CWG2769