This is an unofficial snapshot of the ISO/IEC JTC1 SC22 WG21 Core Issues List revision 113d. See http://www.open-std.org/jtc1/sc22/wg21/ for the official list.

2024-03-20


2322. Substitution failure and lexical order

Section: 13.10.3  [temp.deduct]     Status: CD5     Submitter: Xiang Fan     Date: 2016-09-06

[Accepted as a DR at the June, 2018 (Rapperswil) meeting.]

According to 13.10.3 [temp.deduct] paragraph 7,

The substitution occurs in all types and expressions that are used in the function type and in template parameter declarations. The expressions include not only constant expressions such as those that appear in array bounds or as nontype template arguments but also general expressions (i.e., non-constant expressions) inside sizeof, decltype, and other contexts that allow non-constant expressions. The substitution proceeds in lexical order and stops when a condition that causes deduction to fail is encountered.

However, the same type can be represented in different lexical orders. For example, there is implementation variance on the following example, presumably because of preferring different declarations:

  template <class T> struct A { using X = typename T::X; };

  template <class T> typename T::X f(typename A<T>::X);
  template <class T> auto f(typename A<T>::X) -> typename T::X;
  template <class T> void f(...) { }

  void h() {
    f<int>(0);
  }

Proposed resolution, March, 2018:

Change 13.10.3 [temp.deduct] paragraph 7 as follows:

The substitution occurs in all types and expressions that are used in the function type and in template parameter declarations. The expressions include not only constant expressions such as those that appear in array bounds or as nontype template arguments but also general expressions (i.e., non-constant expressions) inside sizeof, decltype, and other contexts that allow non-constant expressions. The substitution proceeds in lexical order and stops when a condition that causes deduction to fail is encountered. If substitution into different declarations of the same function template would cause template instantiations to occur in a different order or not at all, the program is ill-formed; no diagnostic required. [Note: The equivalent substitution in exception specifications is done only when the noexcept-specifier is instantiated, at which point a program is ill-formed if the substitution results in an invalid type or expression. —end note] [Example:

  template <class T> struct A { using X = typename T::X; };
  template <class T> typename T::X f(typename A<T>::X);
  template <class T> void f(...) { }
  template <class T> auto g(typename A<T>::X) -> typename T::X;
  template <class T> void g(...) { }
  template <class T> typename T::X h(typename A<T>::X);
  template <class T> auto h(typename A<T>::X) -> typename T::X; // redeclaration
  template <class T> void h(...) { }

  void h x() {
    f<int>(0);    // OK, substituting return type causes deduction to fail
    g<int>(0);    // error, substituting parameter type instantiates A<int>
    h<int>(0);    // ill-formed, no diagnostic required
  }

end example]