【问题标题】:Operator in namespace scope hiding another in global scope命名空间范围内的运算符隐藏全局范围内的另一个运算符
【发布时间】:2013-01-16 08:38:30
【问题描述】:

这是编译器错误吗?

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);

【问题讨论】:

  • Yes it is....
  • 不这么认为。 VC++ 生成相同的。
  • @KarthikT:我不确定您的链接代码如何支持“是一个错误”-参数。

标签: c++ namespaces name-lookup


【解决方案1】:

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

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

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

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


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

【讨论】:

  • 我不确定这是否足够详细。如果不匹配,不应该查找下一个封闭的命名空间吗?
  • @phresnel:但是名称operator++ 是如何与operator++ 不匹配的呢?在名称查找期间只有 name 相关。
  • @BartvanIngenSchenau:我是在激怒 litb 来添加这个细节(如果你愿意,试着从提问​​者的角度思考);照原样,我想我已经不能投票了。
  • @phresnel 随时通过编辑来改进答案。拥抱
【解决方案2】:

不,这不是编译器错误。

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

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

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

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

【讨论】:

  • 您描述的第一个项目符号不考虑类范围。也就是说,即使在类成员函数中使用运算符表达式,命名空间范围运算符,即使在与非adl 相关的命名空间范围中,也可以找到,尽管存在同名的成员运算符。
  • @JohannesSchaub-litb:这是 C++03 和 C++11 之间的变化吗?因为我在 C++03 第 3.4.1 条 [basic.lookup.unqual] 中找不到这样的异常。
  • 规则在 13.3.1.2 中指定 :-)
  • 我在编译器之间移植代码。编译器 A 接受这个问题中的代码(它显然也包括用于重载解析的全局运算符) - 这应该被视为编译器错误吗?
  • @MattMcNabb:是的,如果编译器 A 接受这个问题中的代码,当在其一致性模式下调用时,那么编译器 A 就有错误。
【解决方案3】:

重载仅适用于在同一范围内定义的名称。一旦编译器找到匹配的名称,它就不会在外部范围内查找,即使它找到的名称适用于无法使用的东西。这与运营商无关;如果代码以与使用 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
};

【讨论】:

    猜你喜欢
    • 2016-08-05
    • 1970-01-01
    • 2011-01-21
    • 1970-01-01
    • 1970-01-01
    • 2010-09-09
    • 2012-05-03
    • 1970-01-01
    相关资源
    最近更新 更多