15

N3337,“工作草案,C++ 编程语言标准”,在第 13.3.1.2 节中给出了以下示例,p。10:

struct A { };
void operator + (A, A);
struct B {
  void operator + (B);
  void f ();
};
A a;
void B::f() {
  operator+ (a,a);   // error: global operator hidden by member
  a + a;             // OK: calls global operator+
}

但是,这只是一个注释:

注意:表达式中运算符的查找规则与函数调用中运算符函数名的查找规则不同,如下例所示:

我的问题是,标准中的什么地方说这是必须发生的事情,而不是仅仅用一个例子来说明?

据我所知,根据第 13.3.1.2 条,p。2、运算符表达式转换为运算符函数调用。那么为什么以及如何在上面的示例中有所不同呢?

编辑:

调查问题后,我认为我可能忽略了 p。3 和 p.6 在同一条款中共同声明,在查找运算符时,全局候选者和成员候选者被同等考虑(因此,如注释所述,查找规则不同)。但是,我对这个主题的调查是由这个例子引起的,它以与 GCC 4.8 和 Clang 相同的方式编译:

struct X {};  struct Y {};

void operator+(X, X) { }
void operator+(X, Y) { }

void test() {
  void operator+(X, X);
  X x; Y y;

  x + x;  // OK
  x + y;  // OK

  operator+(x, y);  // error
  operator+(x, x);  // OK
}

为什么直接调用操作符函数时块作用域声明会出现阴影,而通过运算符表达式调用时则没有?

以下是来自 GCC 的错误:

operators-main-ss.cpp: In function ‘void test()’:
operators-main-ss.cpp:13:17: error: could not convert ‘y’ from ‘Y’ to ‘X’
   operator+(x, y);  // error
                 ^

这里来自 Clang:

operators-main-ss.cpp:13:16: error: no viable conversion from 'Y' to 'X'
  operator+(x, y);  // error
               ^
operators-main-ss.cpp:1:8: note: candidate constructor (the implicit copy constructor) not viable: no
      known conversion from 'Y' to 'const X &' for 1st argument;
struct X {};  struct Y {};
       ^
operators-main-ss.cpp:7:22: note: passing argument to parameter here
  void operator+(X, X);
                     ^

编译器是否正确让块声明在一种情况下隐藏全局名称而不是另一种情况?

4

3 回答 3

4

你原来的问题:

你的结论是正确的。

operator+(a, a);是一个简单的函数调用,因此编译器以标准顺序搜索潜在的匹配项,找到成员函数并且不再查找。成员函数不匹配且表达式格式错误。

a + a;使用 13.3.1.2 p3 中定义的一组略有不同的规则。首先,一组可能的重载由A::operator+(a). 然后从标准的非限定查找生成可能的重载列表,但忽略成员函数。然后创建一个内置运算符列表。然后使用这 3 个列表来决定最佳匹配。通常,不合格的查找会在第一步之后停止,如果它找到了一些东西,这就是为什么它在通常会失败的地方工作的原因。

问题第 2 部分:

理论上这应该是一样的。没有涉及的成员函数和内置运算符是无关紧要的,因此它们都应该导致标准的非限定查找。我在标准中找不到任何建议它们应该以任何方式不同的东西。除了“它们存在”之外,我在标准中没有看到太多提及块作用域函数声明。毕竟,它们大多只是 C 的遗物。我也没有看到标准中的任何具体规则以一种或另一种方式说明这是否应该有效。该标准确实非常强烈地建议x + y并且operator+(x, y)应该两者都工作或都失败,因为它们都使用相同的方法进行名称查找。

我还没有找到嵌套函数声明的有效用例。

于 2012-12-14T01:53:31.823 回答
1

编译器是否正确让块声明在一种情况下隐藏全局名称而不是另一种情况?

我得出的结论是两个编译器都是错误的。我相信x + y;也应该失败。13.3.1.2p3 明确指出:

非成员候选集是在表达式上下文中根据非限定函数调用 (3.4.2) 中名称查找的通常规则在表达式上下文中非限定查找的结果,除了忽略所有成员函数。

x + y;因此,operator+(x, y);您的示例之间应该没有区别。Commeau online使用代码产生以下错误:

"ComeauTest.c", line 11: error: no operator "+" matches these operands
            operand types are: X + Y
    x + y;  // OK
      ^
"ComeauTest.c", line 13: error: no suitable user-defined conversion from "Y"
 to "X"
          exists
    operator+(x, y);  // error
于 2012-12-14T03:01:20.200 回答
0

关于你的问题第 2 部分

这是标准 c++11 关于 ADL $3.4.2 的引用:

令 X 为由非限定查找 (3.4.1) 生成的查找集,让 Y 为由参数相关查找生成的查找集(定义如下)。如果 X 包含

  • 类成员的声明,或
  • 不是 using 声明的块范围函数声明,或
  • 既不是函数也不是函数模板的声明

那么 Y 为空。

...

看来,这回答了你的问题

于 2013-10-05T14:13:32.933 回答