13

这是编译器错误吗?

template <typename T>
T& operator++(T& t)
{
    return t;
}

namespace asdf {

enum Foo { };
enum Bar { };

Foo& operator++(Foo& foo);

void fun()
{
    Bar bar;
    ++bar;
}

} // end namespace asdf

int main()
{
    return 0;
}

GCC 4.7 错误消息是:

error: no match for 'operator++' in '++bar'
note: candidate is:
note: asdf::Foo& asdf::operator++(asdf::Foo&)
note: no known conversion for argument 1 from 'asdf::Bar' to 'asdf::Foo&'

如果您注释掉该行,它将编译:

Foo& operator++(Foo& foo);
4

3 回答 3

14

不,这不是一个错误。考虑了三组并行的运算符。成员、非成员运算符和内置函数。

非成员的通过正常的非限定+ADL查找来查找,忽略所有类成员函数。因此,全局运算符被更接近的词法隐藏(并且介入的成员函数不会隐藏其他非成员)。

请注意,重载决议发生名称查找1之后;在您的情况下,operator++找到了名称,但没有适当的重载。

如果 Bar 已被全局声明,和/或命名空间 asdf 中的其他运算符,ADL(在前一种情况下)或普通的非限定查找(在后一种情况下)会将运算符拖入。


1Overload resolution (...) takes place after name lookup has succeeded.(C++ 标准)

于 2013-01-16T08:58:02.917 回答
8

不,这不是编译器错误。

对表达式执行了两个名称查找++bar

  • 常规名称查找搜索封闭范围和命名空间,直到找到. operator++此搜索由内而外进行,因此最后搜索全局命名空间。在查找运算符函数时,成员函数被单独处理(并且不要停止此搜索)。
  • 依赖于参数的查找接下来开始并搜索其他类和命名空间,但仅搜索与函数参数相关的那些(operator++在本例中)。

在问题的示例中,正常查找找到asdf::operator++并停止查找。
依赖于参数的查找仅将asdf命名空间添加到要搜索的位置,因为那是enum Bar. 因此,operator++无法找到全局。

您可以operator++使用命名空间中的 using 声明来找到全局asdf

于 2013-01-16T09:03:57.057 回答
1

重载仅适用于在同一范围内定义的名称。一旦编译器找到匹配的名称,它就不会在外部范围内查找,即使它找到的名称适用于无法使用的东西。这与运营商无关;如果代码以与使用 operator++ 相同的方式使用函数名称,则会得到相同的错误。例如:

void f(int);

struct C {
void f(const C&);
void g() {
    f(3); // error: f(const C&) can't be called with argument 3
};
于 2013-01-16T12:06:37.927 回答