简体   繁体   中英

Operator overload resolution work within namespaces

namespace A{
   struct A{
    inline void operator+(const int B) {};   // 1)
 };
    inline void operator+(const A& a,const int B) {};  // 2)
 }

    inline void operator+(const A::A& a,const int B) {};  // 3)

int main()
{
 A::A a;
 a+1; // compile questions
 return 1;
}

The above codes can be compiled without any problem.

But if 1) is commented, it fails to be compiled due to "ambiguous overload for 'operator+' in 'a + 1'" at 2) and 3). I can understand that the operator+ is first searched in class, there is the reason if 1) is not commented, no problem to be compiled. Aren't I right?

Main question: If 1) is commented, why compiler has found one matched operator+, it continues to find the others? I am also curious that which one should be found first as well. (I thought it should stopped immediately based on the information How does the operator overload resolution work within namespaces? )

Second questions:

namespace A{
   struct A{
    inline void operator+(const long int B) {};   // 4), the input parameter type has been changed to long int
 };
    inline void operator+(const A& a,const int B) {};  // 5)
    void test();
 }

    inline void operator+(const A::A& a,const int B) {};  // 6)

 void A::test()
 {
    A a; // a)
    a+ 1; 
 }


int main()
{
 A::A a;
 a+1;  // b) 
 return 1;
}

Compilation ambiguous error at a) due to 4) and 5).

Compilation ambiguous error at b) due to 4) ,5) and 6).

Question i)

For a) and b) , why compiler has already found operator+ with (const long int input parameter type ) at 4), which is member operator funciton in struct A, it still keeps going to find out others. In my mind, compiler should stopped there gives out the type error information. It should be the same as in the case the memeber fucntion's input parameter type is exactly matched, it stops searching for others.

Question 2)

I thought the compiler should stop at 4) and give out type error information. In the case it continues, why member function overloading function (long int) has the same resolution ranking as non-memmber with exact matched input parameter ? In this case, I thought the non-member case with exact matched input parameter should win if compiler decides to continue searching, which makes more sense.

It's explained in C++ standard, section "13.3.1.2 Operators in expressions", point 2:

If either operand has a type that is a class or an enumeration, a user-defined operator function might be declared that implements this operator or a user-defined conversion can be necessary to convert the operand to a type that is appropriate for a built-in operator.

According to the further explanations a+1 would be transformed in either a.operator+(1) (member function) or operator+(a,1) (non-member function).

The point 3 explains that:

for a binary operator @ with a left operand of a type whose cv-unqualified version is T1 and a right operand of a type whose cv-unqualified version is T2, three sets of candidate functions, designated member candidates, nonmember candidates and built-in candidates, are constructed as follows:

  • if T1 is a complete class type, the set of member candidates is the result of the qualified lookup of T1::operator@

This explains why (1) is choosen when it's active. When you comment it out, the set of member candidates is empty. Then, according to the next bullet in the standard:

  • 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 except that all member functions are ignored.

According to these rules, as your expression is in main() and not included in a namespace, the two candidate functions are found and of course, it's ambiguous for the compiler.

You could easily disambiguate which operator to use with the following statement in main() :

    using A::operator+;

If you would have the expression a+1 inside a function in namespace A there would be no ambiguity either :

//...your code preceding the main()
namespace A{  // reopen the namespace 
    void f()
    {
        A a;
        a + 1; // version of the namespace is taken first 
    }
}

The operator definied in the namespace would be taken first (as long as (1) remains commented out).

About second question

First remark: if the member function would use int as argument, like the two other functions, there would be no ambiguity, and (4) would be chosen for both expressions as in your first question. The same would happen if all three functions would use long as argument instead of int .

The fact that you have the same name but different arguments requires us to digg more in depth of name hiding and overloading rules in section 13.3.1.2 of standard. The point 6 states that :

The set of candidate functions for overload resolution is the union of the member candidates, the non-member candidates, and the built-in candidates. The argument list contains all of the operands of the operator. The best function from the set of candidate functions is selected according to 13.3.2 and 13.3.3

13.3.2 is about viable functions, ie having the same number of arguments and an implicit conversion between the argument type and the parameter type.

13.3.3 is about selecting the best viable function. " If there is exactly one viable function that is a better function than all other viable functions, then it is the one selected by overload resolution; otherwise the call is ill-formed ".

In the case of (a) there are two best viable functions: the member function with a conversion (4) or the non member function without conversion, within the namespace (5) (because test() is in the namespace). Hence the ambiguity.

In the case of b) there are three best viable functions: the same as in (a) as well as (6). But note that , if (6) would use long, it would not cause an ambiguity since the member function would win.

Resolving a call to function +, a compiler performs an argument dependent lookup which includes construction of a set of candidate functions and overload resolution. The candidate functions include member, non-member and built-in candidates, and non-member candidates include all declarations found by the unqualified name lookup. If 1 is not commented, normal lookup of a qualified name finds a matching class member function. If 1 is commented, the ADL looks in the set of namespaces associated with the types of the function arguments. It finds two candidates which are both viable functions and have identical ranking of implicit conversion sequences, which results in the ambiguous overload error. The same example is given here http://en.wikipedia.org/wiki/Argument-dependent_name_lookup#Criticism

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM