繁体   English   中英

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

[英]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;
}

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

但是,如果对1)进行了注释,则由于“在2)和3)中“'a + 1'中'operator +'的重载不明确”而无法编译。 我可以理解,运算符+是在类中首先搜索到的,这是因为如果1)没有被注释,则编译没有问题。 我不是吗

主要问题:如果注释了1),为什么编译器找到了一个匹配的运算符+,却继续找到其他运算符? 我也很好奇应该首先找到哪个。 (我认为应根据以下信息立即停止操作:运算符重载解析如何在名称空间中工作?

第二个问题:

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

由于4)和5),a)处的编译歧义错误。

由于4),5)和6),b)处的编译歧义错误。

问题i)

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

问题2)

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

在C ++标准的“ 13.3.1.2表达式中的运算符”部分,第2点中对此进行了说明:

如果任何一个操作数的类型是类或枚举,则可能会声明一个用户定义的运算符函数来实现该运算符,或者可能需要用户定义的转换才能将该操作数转换为适合于在运算符中。

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

第3点说明:

对于二进制运算符@,其左操作数的类型为cv不合格版本为T1,右操作数的类型为cv不合格版本为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)的情况下,有两个最佳可行的函数:在名称空间(5)中具有转换的成员函数(4)或没有转换的非成员函数(因为test()在名称空间中)。 因此,模棱两可。

对于b),存在三个最佳可行功能:与(a)和(6)中相同。 但是请注意,如果(6)将使用long,则不会引起歧义,因为成员函数将获胜。

解决对函数+的调用,编译器执行基于参数的查找,其中包括一组候选函数的构造和重载解析。 候选函数包括成员,非成员和内置候选,非成员候选包括通过不合格名称查找找到的所有声明。 如果未注释1,则正常查找合格名称会找到匹配的类成员函数。 如果注释为1,则ADL查找与函数参数类型关联的名称空间集合。 它找到两个候选函数,它们都是可行的函数,并且具有相同的隐式转换序列等级,从而导致模棱两可的过载错误。 此处提供了相同的示例http://en.wikipedia.org/wiki/Argument-dependent_name_lookup#Criticism

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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