Two expressions involving template parameters are considered
equivalent
if two function definitions containing the expressions would satisfy
the
one-definition rule, except that the tokens used
to name the template parameters may differ as long as a token used to
name a template parameter in one expression is replaced by another token
that names the same template parameter in the other expression
. Two unevaluated operands that do not involve template parameters
are considered equivalent
if two function definitions containing the expressions
would satisfy the one-definition rule,
except that the tokens used to name types and declarations may differ
as long as they name the same entities, and
the tokens used to form concept-ids (
[temp.names]) may differ
as long as the two
template-ids are the same (
[temp.type])
. [
Note 3:
For instance,
A<42> and
A<40+2> name the same type
. —
end note]
[
Note 4:
The intent is to avoid
lambda-expressions appearing in the
signature of a function template with external linkage
. —
end note]
For determining whether two dependent names (
[temp.dep]) are equivalent,
only the name itself is considered, not the result of name lookup
. [
Note 5:
If such a dependent name is unqualified,
it is looked up from
a first declaration of the function template (
[temp.res.general])
. —
end note]
[
Example 3:
template <int I, int J> void f(A<I+J>);
template <int K, int L> void f(A<K+L>);
template <class T> decltype(g(T() h();
int g(int);
template <class T> decltype(g(T() h()
{ return g(T(); }
int i = h<int>();
template <int N> void foo(const char (*s)[([]{}, N)]);
template <int N> void foo(const char (*s)[([]{}, N)]);
template <class T> void spam(decltype([]{}) (*s)[sizeof(T)]);
template <class T> void spam(decltype([]{}) (*s)[sizeof(T)]);
—
end example]
Two potentially-evaluated expressions involving template parameters that are not equivalent are
functionally equivalent
if, for any given set of template arguments, the evaluation of the
expression results in the same value
. Two unevaluated operands that are not equivalent
are functionally equivalent if, for any given set of template arguments,
the expressions perform
the same operations in the same order with the same entities
. [
Note 6:
For instance, one could have redundant parentheses
. —
end note]
[
Example 4:
template<int I> concept C = true;
template<typename T> struct A {
void f() requires C<42>;
void f() requires true;
};
—
end example]
Two
template-parameters are
equivalent
under the following conditions:
- they declare template parameters of the same kind,
- if either declares a template parameter pack, they both do,
- if they declare constant template parameters,
they have equivalent types
ignoring the use of type-constraints for placeholder types, and
- if they declare template template parameters,
their template-heads are equivalent.
When determining whether types or
type-constraints
are equivalent, the rules above are used to compare expressions
involving template parameters
. If the validity or meaning of the program depends on
whether two constructs are equivalent, and they are
functionally equivalent but not equivalent, the program is ill-formed,
no diagnostic required
. Furthermore, if two declarations
A and
B of function templates
- introduce the same name,
- have corresponding signatures ([basic.scope.scope]),
- would declare the same entity,
when considering A and B to correspond in that determination ([basic.link]), and
- accept and are satisfied by the same set of template argument lists,
but do not correspond,
the program is ill-formed, no diagnostic required
.[
Note 7:
This rule guarantees that equivalent declarations will be linked with
one another, while not requiring implementations to use heroic efforts
to guarantee that functionally equivalent declarations will be treated
as distinct
. For example, the last two declarations are functionally
equivalent and would cause a program to be ill-formed:
template <int I> void f(A<I>, A<I+10>);
template <int I> void f(A<I>, A<I+10>);
template <int I> void f(A<I>, A<I+10>);
template <int I> void f(A<I>, A<I+11>);
template <int I> void f(A<I>, A<I+10>);
template <int I> void f(A<I>, A<I+1+2+3+4>);
—
end note]