28

我试图编写一个既可用作前缀又可用作后缀运算符的运算符

#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;
}

GCC 可以编译并正常工作,但是 clang 说

main.cpp:9:24:错误:重载后增量运算符的参数必须具有类型“int”(不是“T ...”)

void operator++(B, T...) {

谁是对的?


感谢任何帮助我了解 GCC 行为的人。我提交了一份新的 Clang 错误报告:

http://llvm.org/bugs/show_bug.cgi?id=14995

4

1 回答 1

7

原始答案:(删除,因为它可能包含有用的信息

我想说这一切都归结为重载的运算符模板是否被认为是重载的运算符。从逻辑上讲,我认为情况并非如此,Clang 是错误的:我认为应该首先根据名称和签名兼容性选择模板作为重载解析的候选者,然后实例化,然后(可能)选择。在我看来,只有在实例化之后,编译器才应该检查生成的函数是否具有适当数量的参数。

但这只是我的意见。根据关于后缀重载的第 13.5.7/1 节operator ++

“如果函数是具有一个参数(应为 int 类型)的成员函数或具有两个参数(第二个应为 int类型)的非成员函数,则它为对象定义了后缀增量运算符 ++那种"

该标准似乎没有阐明函数模板是否应被视为涉及对合法运算符重载签名的限制的函数(至少,我找不到任何解决这种歧义的句子)。只要这是真的,这个问题就很难给出明确的答案,我们只剩下意见了。

但我想提一下这个问题的另一个相关方面:一致性

尽管问题文本中的代码确实无法在 Clang 上编译,但以下代码可以:

template<typename... Ts>
int operator + (X x1, Ts... args)
{
    return 0;
}

我看不出这两种情况之间有任何概念上的区别:如果要在实例化之前检查运算符重载的签名,那么上述定义也不应该编译。如果不是这种情况,则应该编译问题文本中的代码。

所以在我看来,要么GCC是对的,要么它们都是错的。

更新:

正如@JesseGood 和@SethCarnegie 正确指出的那样,根据 14.7/4:

“特化是被实例化或显式特化的类、函数或类成员。”

此外,根据 14.6/8:

“对于可以生成有效专业化的模板,不应发出诊断。”

Thus, it seems Clang is indeed wrong and no compilation error shall be produced for the operator function template in the question's text.

于 2013-01-17T11:38:46.133 回答