由于超类(按值传递)导致重载构造函数调用不明确

Ambiguous call of overloaded constructor due to super class (pass by value)

本文关键字:重载 函数调用 不明确 按值传递 于超类 超类      更新时间:2023-10-16

我为GSL的某些部分编写了一个小的c++包装器,并遇到了以下难题(对我来说)。代码(简化到最基本的部分)如下:

    #include <stdlib.h>
    struct gsl_vector_view {};
    class Vector : protected gsl_vector_view {
            public:
            Vector ( const Vector& original );
            Vector ( const gsl_vector_view view );
    };
    class AutoVector : public Vector {
            public:
            explicit AutoVector ( const size_t dims );
    };
    void useVector ( const Vector b ) {}
    void test () {
            const AutoVector ov( 2 );
            useVector( ov );
    }

不能在GCC 4.4.5下编译g++ -c v.cpp但收益率

     In function ‘void test()’:
    19: error: call of overloaded ‘Vector(const AutoVector&)’ is ambiguous
    7: note: candidates are: Vector::Vector(gsl_vector_view)
    6: note:                 Vector::Vector(const Vector&)
    19: error:   initializing argument 1 of ‘void useVector(Vector)’

我很惊讶受保护的基类gsl_vector_view被useVector(Vector)的调用所考虑。我本以为useVector属于"c++编程语言"第3版第405页中所说的"一般公众",因此无法访问受保护的信息,因此不会被它混淆。我知道可以通过将构造函数声明为

来消除歧义
    explicit Vector ( const gsl_vector_view view );

我不知道(老实说,也不理解)的是,当我将构造函数声明为

时,重载调用的模糊性就消失了。
    Vector ( const gsl_vector_view& view );

。通过引用传递参数(无论如何我认为这样做是正确的)

重载解析是在访问检查之前完成的,这就是为什么即使是受保护的基类成员也要考虑。

重载解析在标准的13.3章中有描述。我的解释是,将const AutoVector ov绑定到Vector ( const Vector& original );用户定义的转换,一种派生到基的转换([13.3.3.1.4/1])类型。对于Vector ( const gsl_vector_view view );,转换顺序也是自定义转换,因为它是左值到右值的转换,然后是用户自定义转换。因此,两个转换序列被认为是相等的,没有一个比另一个好,因此你得到了歧义。

现在,如果您将变量更改为Vector ( const gsl_vector_view& view );,那么这两个转换都是左值到值的转换,然后是用户定义的转换(派生到基的转换)。这两个可以排序([13.3.3.2/4]),转换为const Vector&被认为更好,因此没有歧义。

这个问题与protected继承或构造函数本身无关。对于普通的函数调用(无论继承是什么),这个问题也会持续存在。

当你在所有的重载版本中通过引用传递时,那么最接近的基类被选择(如果有超过一个基类是最接近的,那么它是病态的)。

在按值传递的情况下,所有的函数都被认为是同样好的候选函数。因此,您会得到这个编译错误。有一小段引自《标准》,在某种程度上符合你的问题。

§13.3.1 (5)
…声明的非静态成员函数reff限定符,一个额外的规则适用:-即使隐式对象形参不是const限定的,则可以将右值绑定到参数,只要在所有其他方面参数可以是转换为隐式对象形参的类型。