【发布时间】:2020-08-21 14:54:18
【问题描述】:
以下所有标准引用均指current ISO Standard Working Draft,生成于 2020-06-22。
[dcl.fct]/18 声明[摘录,强调我的]:
缩写函数模板是一种具有一个或多个通用参数类型占位符 ([dcl.spec.auto]) 的函数声明。 缩写函数模板等价于函数模板 ([temp.fct]),其template-parameter-list 包含一种发明类型template-parameter 对于函数声明的每个泛型参数类型占位符,按出现顺序。 [...]
使得以下函数声明是等价的:
template <typename T>
void f(T);
void f(auto); // re-declaration
然而,我们可能会注意到,[dcl.fct]/18 的例子表明
[...]
这些声明与以下声明功能等效(但不等效)。
[...]
这可能(我不确定如何解释)与前面段落中的等价陈述相冲突。
现在,GCC 10.1.0 和 Clang 10.0.0(以及 GCC:HEAD 和 Clang:HEAD)在这里都有一些混合行为。如果我们声明一个函数模板,然后使用混合的经典函数模板语法和缩写函数模板语法来定义它(/重新声明它),Clang 接受大多数情况(定义先前声明的函数)而 GCC 拒绝所有情况(参见) 重新声明为单独声明的函数,随后在重载解析中出现歧义失败):
// 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.
}
奇怪的是,如果我们将函数模板设为类成员函数模板,GCC 和 Clang 都接受 A1 到 D1 的情况,但都拒绝最后的情况 E1以上:
// 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) {}
带有以下错误消息:
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)'叮当
error: out-of-line definition of 'e' does not match any declaration in 'Se'
现在,类型模板参数的 name 不需要与函数模板的重新声明(或定义)保持一致,只需命名泛型类型占位符即可。
GCC 的错误消息特别有趣,暗示发明的类型模板参数被视为具体类型而不是泛型类型占位符。
问题:
- 对于从 A1 到 D1 的案例(分别为拒绝和接受),GCC 和 Clang 中哪一个是正确的? GCC 和 Clang 拒绝上述情况 E2 是否正确? (工作草案的)哪些标准段落明确支持它们?
【问题讨论】:
标签: c++ templates language-lawyer c++20