运算符重载解析在命名空间内工作

时间:2014-10-22 21:23:06

标签: c++ templates namespaces overloading

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

上述代码可以毫无问题地编译。

但是如果1)被评论,则由于“a + 1”中的'运算符+'在2)和3)处的模糊重载而无法编译。我可以理解,操作符+是在课堂上首次搜索的,如果1)没有被评论,没有问题需要编译。我不对吗?

主要问题:如果1)被评论,为什么编译器找到了一个匹配的运算符+,它继续找到其他运算符?我也很好奇首先应该找到哪一个。 (我认为它应该根据信息How does the operator overload resolution work within namespaces?

立即停止

第二个问题:

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

a)由于4)和5)编译模糊错误。

b)编译模糊错误由于4),5)和6)。

问题i)

对于a)和b),为什么编译器已经在4)找到了operator + with(const long int input parameter type),它是struct A中的成员运算符函数,它仍然会继续查找其他函数。在我看来,编译器应该停在那里给出类型错误信息。它应该与memeber fucntion的输入参数类型完全匹配的情况相同,它会停止搜索其他类型。

问题2)

我认为编译器应该停在4)并给出类型错误信息。 在它继续的情况下,为什么成员函数重载函数(long int)具有与非memmber相同的分辨率排名和精确匹配的输入参数?在这种情况下,如果编译器决定继续搜索,我认为具有完全匹配输入参数的非成员情况应该获胜,这更有意义。

2 个答案:

答案 0 :(得分:1)

它在C ++标准中解释,部分" 13.3.1.2表达式中的运算符",第2点:

  

如果任一操作数的类型是类或枚举,则a   可以声明用户定义的操作符函数来实现它   可能需要运算符或用户定义的转换来转换   操作数适用于内置运算符的类型。

根据进一步的解释,a+1将在a.operator+(1)(成员函数)或operator+(a,1)(非成员函数)中进行转换。

第3点解释说:

  

表示二元运算符@,其左操作数的类型为   cv-unqualified version是T1和一个类型的右操作数   cv-unqualified version是T2,三套候选函数,   指定成员候选人,非成员候选人和内置   候选人的构建如下:

     
      
  • 如果T1是完整的类类型,则候选成员集合是T1 :: operator @
  • 的限定查找的结果   

这解释了为什么(1)在激活时被选中。当您将其注释掉时,成员候选人集合为空。然后,根据标准中的下一个项目符号:

  
      
  • 非成员候选者的集合是在表达式的上下文中根据运算符@的非限定查找的结果。   通常的非限定函数调用中的名称查找规则,除了   所有成员函数都被忽略。
  •   

根据这些规则,由于您的表达式位于main()中且未包含在命名空间中,因此找到了两个候选函数,当然,它对于编译器来说是不明确的。

您可以轻松消除使用哪个运算符与main()中的以下语句:

    using A::operator+;

如果在命名空间A中的函数内部使用表达式a + 1,则不存在歧义:

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

将首先获取在命名空间中定义的运算符(只要(1)仍然被注释掉)。

关于第二个问题

第一句话:如果成员函数使用int作为参数,就像其他两个函数一样,没有歧义,并且(4)将在第一个问题中为两个表达式选择。如果所有三个函数都使用long作为参数而不是int,则会发生同样的情况。

您拥有相同名称但不同的参数这一事实要求我们在标准的第13.3.1.2节中深入挖掘名称隐藏和重载规则。第6点指出:

  

重载解析的候选函数集是。的并集   会员候选人,非会员候选人和内置人员   候选人。参数列表包含的所有操作数   运营商。候选函数集中的最佳函数是   根据13.3.2和13.3.3选择

13.3.2是关于可行的函数,即具有相同数量的参数以及参数类型和参数类型之间的隐式转换。

13.3.3是关于选择最佳可行功能。 " 如果只有一个可行功能比所有其他可行功能更好,那么它就是 一个由重载决议选择的;否则电话不正确"。

在(a)的情况下,有两个最可行的函数:带有转换的成员函数(4)或没有转换的非成员函数,在命名空间(5)中(因为test()在命名空间中) 。因此含糊不清。

在b)的情况下,有三个最佳可行功能:与(a)和(6)中相同。但请注意,如果(6)使用long,则不会导致歧义,因为成员函数会获胜。

答案 1 :(得分:-1)

解析对函数+的调用,编译器执行参数依赖查找,其中包括构造一组候选函数和重载决策。候选函数包括成员,非成员和内置候选者,非成员候选者包括由非限定名称查找找到的所有声明。如果未注释1,则正常查找限定名称将查找匹配的类成员函数。如果注释为1,则ADL查找与函数参数类型关联的名称空间集。它找到两个候选者,它们都是可行的函数并且具有相同的隐式转换序列排名,这导致模糊的过载错误。这里给出了同样的例子http://en.wikipedia.org/wiki/Argument-dependent_name_lookup#Criticism

相关问题