Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rules for lookup of operators in C++11

Tags:

c++

c++11

N3337, "Working Draft, Standard for Programming Language C++," gives the following example in clause 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+
}

However, this is just a note:

Note: The lookup rules for operators in expressions are different than the lookup rules for operator function names in a function call, as shown in the following example:

My question is where in the standard does it say that this is what has to happen as opposed to just having the note with an example?

As far as I can tell, according to clause 13.3.1.2, p. 2, operator expressions are converted to operator function calls. So why and how should there be a difference in the example above?

Edit:

After looking into the problem, I think that I may have overlooked p. 3 and p.6 in the same clause that together state that global candidates and member candidates are considered equally when looking up operators (thus lookup rules are different as the note says). However, my inquiry into this subject was stemmed by this example that compiles in the same way with GCC 4.8 and 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
}

Why is there shadowing by the block scope declaration when the operator function is called directly but not when it is called by operator expression?

Here are the errors from 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
                 ^

And here from 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);
                     ^

Are the compilers correct to have the block declaration shadow the global name in one case but not the other?

like image 898
Marcin Zalewski Avatar asked Dec 13 '12 21:12

Marcin Zalewski


2 Answers

Your original question:

Your conclusion is correct.

operator+(a, a); is a simple function call so the compiler searches for potential matches in the standard order, finds the member function and never looks any further. The member function doesn't match and the expression is ill formed.

a + a; uses a slightly different set of rules as defined in 13.3.1.2 p3. First a set of possible overloads is produced from A::operator+(a). Then a list of possible overloads is produced from a standard unqualified lookup but member functions are ignored. A list of builtin operators is then created. These 3 lists are then used to decide the best match. Normally, unqualified lookup would stop after the first step if it found something which is why this works where it would normally fail.

Question part 2:

In theory this should be the same. No member functions involved and builtin operators are irrelevant so they should all result in a standard unqualified lookup. I can't find anything in the standard to suggest they should be in any way different. I don't see much mention of block scope function declarations in the standard aside from "they exist". They are mostly just a relic of C after all. I also don't see any specific rules in the standard that state one way or the other about whether this should work or not. The standard does suggest very strongly that x + y and operator+(x, y) should either both work or both fail since they both use the same method for name lookup.

I also have yet to find a valid use case for nested function declarations.

like image 61
John5342 Avatar answered Nov 19 '22 10:11

John5342


Are the compilers correct to have the block declaration shadow the global name in one case but not the other?

I have come to the conclusion that both compilers are wrong. I believe x + y; should also fail. 13.3.1.2p3 states it clearly:

The set of non-member candidates is the result of the unqualified lookup of operator@ in the context of the expression according to the usual rules for name lookup in unqualified function calls (3.4.2) except that all member functions are ignored.

As a result, there should be no difference between x + y; and operator+(x, y); in your example. Commeau online produces the following errors with the code:

"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
like image 40
Jesse Good Avatar answered Nov 19 '22 11:11

Jesse Good