The latest standard draft N4910 has this example in [temp.over.link] regarding functional equivalence:
template<int I> concept C = true;
template<typename T> struct A {
void f() requires C<42>; // #1
void f() requires true; // OK, different functions
};
My understanding is that this is ok, since C<42> and true are unevaluated operands. Therefore according to [temp.over.link]/5 when considering whether the constraints are functionally equivalent, not the result of the expressions, but what operations are performed on which entity and in what order, is deciding functional equivalency of the constraints.
However if the constraints were functionally equivalent, then because they are not equivalent, by [temp.over.link]/7 the program would be ill-formed, no diagnostic required, as declaring the same member twice would make the program ill-formed.
On the other hand
template<typename>
requires C<42>
void g() {};
template<typename>
requires true
void g() {};
seems to be ill-formed, no diagnostic required, because [temp.over.link]/6 says that template heads are functionally equivalent if they accept and satisfy the same template arguments.
Am I misunderstanding the example and referenced standard wording or is there really such a difference? If so, why?
It's to avoid having to mangle constraints into function symbols (see: Why decltype expressions in return types have to be mangled in the symbol name?).
Consider the case where the two function templates are in different TUs, x.cpp and y.cpp:
// x.cpp
template<int> concept C = true;
template<typename> requires C<42> int g() { return 1; }
int x() {
volatile auto p = g<int>;
return p();
}
// y.cpp
template<typename> requires true int g() { return 2; };
int y() {
volatile auto p = g<int>;
return p();
}
If this were to be valid, we'd need to mangle constraints into function template instantiation symbols, but we really (really) don't want to do that, since it would be a huge pain for ABI compatibility (we'd never be able to tighten, relax or even refactor constraints) as well as a mild headache for implementors. But it can't be ODR ill-formed NDR, since to make this an ODR violation we'd have to determine that the two function templates are functionally equivalent, which is undecidable (Church). So we make equivalent-but-not-functionally-equivalent its own IFNDR.
However, this problem does not arise for class template member functions; a class cannot redeclare member functions, and must be completed within a TU, and class definitions across TUs must be token-level equivalent. So it's no problem to allow the two distinct member function declarations, since they can't be referred to anyway. Well... - https://github.com/cplusplus/CWG/issues/256
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With