All standard references below refers to the current ISO Standard Working Draft, generated on 2020-06-22.
[dcl.fct]/18 states that [extract, emphasis mine]:
An abbreviated function template is a function declaration that has one or more generic parameter type placeholders ([dcl.spec.auto]). An abbreviated function template is equivalent to a function template ([temp.fct]) whose template-parameter-list includes one invented type template-parameter for each generic parameter type placeholder of the function declaration, in order of appearance. [...]
Such that the following to function declarations are areuably equivalent:
template <typename T>
void f(T);
void f(auto); // re-declaration
We may note, however, that the example of [dcl.fct]/18 states that
[...]
These declarations are functionally equivalent (but not equivalent) to the following declarations.
[...]
which may arguably (I'm unsure how interpret this) conflict with the equivalence statement in the prior passage.
Now, both GCC 10.1.0 and Clang 10.0.0 (as well as GCC:HEAD and Clang:HEAD) have some mixed behavior here. If we declare a function template and later define it (/re-declare it) using a mixed classical function template syntax with abbreviated function template syntax, Clang accepts most cases (defining a previously declared function) whereas GCC rejects all (sees the (attempted) re-declarations as separately declared functions with subsequent ambiguity failures in overload resolution):
// A1: Clang OK, GCC error
template <typename T>
void a(T);
void a(auto) {}
// B1: Clang OK, GCC error
void b(auto);
template <typename T>
void b(T) {}
// C1: Clang OK, GCC error
template <typename T, typename U>
void c(T, U);
void c(auto, auto) {}
// D1: Clang OK, GCC error
template <typename T, typename U>
void d(T, U);
template <typename T>
void d(T, auto) {}
// E1: Clang error, GCC error
template <typename T>
void e(T, auto);
template <typename T>
void e(auto, T) {}
int main() {
a(0); // Clang OK, GCC error.
b(0); // Clang OK, GCC error.
c(0, '0'); // Clang OK, GCC error.
d(0, '0'); // Clang OK, GCC error.
e(0, '0'); // Clang error, GCC error.
}
Curiously, if we make the function template a class member function template, both GCC and Clang accepts cases A1 through D1, but both rejects the final case E1 above:
// A2: OK
struct Sa {
template <typename T>
void a(T);
};
void Sa::a(auto) {}
// B2: OK
struct Sb {
void b(auto);
};
template <typename T>
void Sb::b(T) {}
// C2: OK
struct Sc {
template <typename T, typename U>
void c(T, U);
};
void Sc::c(auto, auto) {}
// D2: OK
struct Sd {
template <typename T, typename U>
void d(T, U);
};
template <typename T>
void Sd::d(T, auto) {}
// E2: Error
struct Se {
template <typename T>
void e(T, auto);
};
template <typename T>
void Se::e(auto, T) {}
with the following error messages:
GCC
error: no declaration matches 'void Se::e(auto:7, T)' note: candidate is: 'template<class T, class auto:6> void Se::e(T, auto:6)'Clang
error: out-of-line definition of 'e' does not match any declaration in 'Se'
Now, the name of a type template parameter is not required to be consistent over re-declaration (or a definition) of a function template, as just names a generic type placeholder.
GCC's error message is particularly interesting, hinting that invented type template parameters are treated as concrete types rather than generic type placeholders.
Question:
- Which of GCC and Clang are correct regarding cases A1 through D1 (rejecting and accepting, respectively)? Are GCC and Clang correct to reject case E2 above? What standard passage (of the working draft) unambiguously supports them?