【问题标题】:Why are these overloaded function calls ambiguous?为什么这些重载的函数调用模棱两可?
【发布时间】:2012-08-06 12:16:31
【问题描述】:

为什么下面的重载函数调用不明确?出现编译错误:

重载'test(long int)'的调用不明确,候选者是:void test(A)| 无效测试(B)|

代码:

class A
{
    public:
        A(int){}
        A(){}
};

class B: public A
{
    public:
        B(long){}
        B(){}
};

void test(A a)
{
}

void test(B b)
{
}

void main()
{
    test(0L);
    return;
}

【问题讨论】:

    标签: c++ implicit-conversion overload-resolution


    【解决方案1】:

    您遇到了一个错误,因为重载解析必须从两个同样可行的函数中进行选择(两者都有用户定义的转换)。函数重载解析是一个非常复杂的主题。有关重载解决方案的更多详细信息,请参见例如这个recent lecture 由 Stephan T. Lavavej 提供。通常最好创建单参数构造函数explicit,然后使用显式构造函数参数调用您的函数。

    test(0L) 与任何重载都不完全匹配,因为没有重载 test(long)。您提供的两个重载都对其参数进行了用户定义的转换,但编译器认为它们同样可行。 A 重载必须执行标准转换(long 到 int),然后是用户定义的转换(int 到 A),B 重载用户定义的转换(long 到 B)。但两者都是用户定义的隐式转换序列

    这些排名如何?该标准在13.3.3.2 Ranking implicit conversion sequences [over.ics.rank]

    中说

    标准转换序列 S1 比转换序列更好 如果 S1 是 S2 的适当子序列,则标准转换序列 S2

    这些类型的平局,例如如果 A 是 B 的派生类,则适用(反之亦然)。但是这里没有一个转换序列是另一个的子序列。因此它们同样可行,编译器无法解析调用。

    class A
    {
    public:
        explicit A(int){}
        A(){}
    };
    
    class B: public A
    {
    public:
        explicit B(long){}
        B(){}
    };
    
    void test(A a)
    {}
    
    void test(B b)
    {}
    
    int main()
    {
        test(A(0L));  // call first overload
        test(B(0L)); // call second overload
        return 0;
    }
    

    注意:这是int main(),而不是void main()

    【讨论】:

    • +1,这是最正式的正确答案,尽管您可以稍微扩展一下解释。
    • @rhalbersma:但我认为 test(0L) 与 test(B b) 更精确匹配?为什么会模棱两可?
    • 0Llong,所以你的第二段应该说“没有test(long)”。
    • @huwang 查看更新的答案:只有当 long->B 是 long->int->A 的子序列,反之亦然,B 重载才会更接近。
    【解决方案2】:

    函数重载会考虑精确的参数类型或隐式转换。 在您的示例中,从重载的角度来看,替代 A(0L) 和 B(0L) 都是相同的,因为需要隐式构造函数调用。

    【讨论】:

      【解决方案3】:

      您正在使用 long 类型的参数调用 test。

      没有测试(长)。

      编译器必须在 test(A) 和 test(B) 之间进行选择。

      调用 test(A) 它有一个 long -> int -> A 的转换序列。

      调用 test(B) 它有一个 long -> B 的转换序列。

      根据标准的排名规则,如果一个排名比另一个好,它会选择一个 - 或者它会因模棱两可而失败。

      在这种特定情况下,两个转换序列的排名相同。

      13.3.3“最佳可行函数”部分中,标准中有一长串关于如何计算转换序列排名的规则

      【讨论】:

      • 在标准中。我会帮你查的,等一下。
      • 但它没有解释为什么不搜索最接近的潜在转化树。
      • 我已经更新了我的答案,其实我说的太简单了。有一种方法可以对它们进行排名。在这种情况下,两个转换序列的排名是相同的。
      【解决方案4】:

      试试这个:

      class A
      {
        public:
        explicit A(int){}
        A(){}
      };
      

      关键字显式停止编译器进行隐式转换。

      【讨论】:

        【解决方案5】:

        编译器只允许对用户类型进行一次隐式转换。如果这还涉及原始类型之间的转换,则它们不计算在内。即使您在 test(B) 的情况下进行了两次转换,但以下内容将无法编译:

        class B
        {
        public:
            B(int) {}
        };
        
        class A
        {
        public:
            A(const B&) {}
        };
        
        void test(const A&) {}
        
        ....
        
        test(5);
        

        要禁用编译器进行隐式转换,您应该使用 explicit 关键字和构造函数

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-08-01
          • 2023-03-24
          • 2016-03-08
          相关资源
          最近更新 更多