问题描述
以下所有标准参考文献均指2020年6月22日生成的current ISO Standard Working Draft。
[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,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,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) {}
海湾合作委员会
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)'
C语
error: out-of-line deFinition of 'e' does not match any declaration in 'Se'
现在,类型模板参数的 name 不需要在函数模板的重新声明(或定义)上保持一致,就像命名通用类型占位符一样。
GCC的错误消息特别有趣,暗示将发明的类型模板参数视为具体类型,而不是通用类型占位符。
问题:
- 在案例 A1 至 D1 (分别为拒绝和接受)中,哪个GCC和Clang是正确的? GCC和Clang是否正确拒绝上述案例 E2 ? (工作草案中的)哪段标准明确支持它们?
解决方法
此:
get_schema
翻译为:
template <typename T>
void e(T,auto);
通过对比,
template<typename T,typename U> void e(T,U);
翻译为:
template <typename T>
void e(auto,T) {}
请记住abbreviated function template parameters are placed at the end of the template parameter list。因此,由于反转了模板参数的顺序,因此它们没有声明相同的模板。第一个声明一个模板,第二个声明并定义一个不同模板。
您不会因此而获得编译错误,因为第二个定义也是声明。但是,当您使用类成员时,成员外的定义不是声明。因此,它们必须具有匹配的成员内声明。他们没有因此出现错误。
对于其他的,“功能等效(但不等效)”文本是非规范性表示法。您引用的实际规范文本清楚地表明,这些是“等效的”,而不仅仅是“功能上等效的”。而且由于每个[temp.over.link]/7都使用了“等价”一词来匹配声明和定义,因此在我看来,该标准指出A到D的情况很好。
奇怪的是,该非规范性文本为introduced by the same proposal that introduced the normative text。但是,它建议从seems clear that it means "equivalent",not "functionally equivalent"继承template <typename T,typename U>
void e(U,T) {}
语法。
因此,就规范性文字而言,一切似乎都很清楚。但是,非规范性矛盾的存在说明规范上存在编辑问题或实际缺陷。
尽管标准本身在措辞上很明确且在规范上是合理的,但看来这不是标准的编写者所打算的。
P0717引入了“功能等效”的概念,以区别于“等效”。该提议被接受。但是,P0717是在采用Concepts TS for C ++ 20的早期引入的。在该提案中,它专门提到了简洁的模板语法,EWG明确投票赞成采用“功能等效”的措词,而不是Concepts TS“等效”的措词。
也就是说,P0717明确要求委员会打算要求用户使用一致的语法。
但是,Concepts TS中的简洁模板语法已从C ++ 20中删除(或者从未真正添加过)。这意味着任何“功能上等同的”措辞都不会加入,因为该功能从未加入过。
随后发生了P1141,它添加了缩写的模板语法,这涵盖了Concepts TS简洁模板语法的大部分内容。但是,尽管P0717的作者之一是P1141的作者,但显然有人在措词上犯了一个错误,没有人抓住它。这可以解释为什么非规范性文本指出缺乏真正的对等:因为这实际上是委员会的意图。
因此,这很有可能是规范性文本中的错误。