我尝试编写一个可以用作前缀和后缀运算符的运算符
#include <iostream>
#include <utility>
struct B {
// ...
};
template<typename ...T>
void operator++(B, T...) {
std::cout << ((sizeof...(T) == 0) ? "prefix" : "postfix") << std::endl;
}
int main() {
B b;
b++;
++b;
}
海湾合作委员会汇编并正常运作,但是clang说
main.cpp:9:24:错误:重载后增量运算符的参数必须是'int'类型(不是'T ...')
void operator++(B, T...) {
谁是对的?
感谢所有帮助我了解GCC行为的人。我提交了一份新的Clang错误报告:
答案 0 :(得分:7)
原始答案:(未删除,因为它可能包含有用的信息)
我想说这一切都归结为重载运算符模板是否被视为重载运算符。从逻辑上讲,我认为情况并非如此,并且Clang是错的:我认为应首先选择作为基于名称和签名兼容性的重载决策候选者,然后实例化,然后(可能)被选中。我看到它的方式,只有在实例化之后,编译器才应检查生成的函数是否具有适当数量的参数。
但那只是我的意见。关于后缀operator ++
的重载,见§13.5.7/ 1:
“如果函数是具有一个参数(应为int类型)的成员函数或具有两个参数的非成员函数(第二个参数应为类型) int),它为该类型“
的对象定义后缀增量运算符++标准似乎没有说明功能模板是否应被视为功能,以解决对合法运营商超载签名的限制(至少,我找不到任何解决这种歧义的句子。只要这是真的,这个问题很难给出明确的答案,我们留下意见。
但我想提及此问题的另一个相关方面:一致性。
虽然问题文本中的代码确实不能在Clang上编译,但以下情况确实如此:
template<typename... Ts>
int operator + (X x1, Ts... args)
{
return 0;
}
我认为这两种情况之间没有任何概念上的区别:如果要在实例化之前检查运算符重载的签名,那么上面的定义也不应该编译。如果不是这种情况,那么问题文本中的代码应该编译。
所以我认为答案是GCC是正确的,或者它们都是错的。
<强>更新强>
@JesseGood和@SethCarnegie正确地指出,每14.7 / 4:
“专门化是实例化或显式专用的类,函数或类成员。”
此外,按照14.6 / 8:
“不能为可以生成有效专业化的模板发出诊断。”
因此,对于问题文本中的运算符函数模板,似乎Clang确实是错误的并且不会产生编译错误。