选择性构造函数继承的“不明确的解决方案”错误

时间:2014-11-23 19:01:34

标签: c++ inheritance c++11 constructor overloading

我试图整理一些代码。

我有16个类,所有这些类都有一些共同的功能,我使用宏来抽象:

#define COMMON4( CLASS, BASE, ASSIGN, CHECK ) \
    explicit CLASS( PyObject* pyob, bool owned=false ) \
        : BASE{ pyob, owned } { \
        validate(); } \
        \
    CLASS& operator=( const Object& rhs  ) { \
        return *this = rhs.ptr(); } \
    \ 
    CLASS& operator=( PyObject* rhsp ) { \
        if(ptr()!=rhsp) set(ASSIGN);  return *this; }  \
        \
    bool accepts( PyObject* pyob ) const override { \
        return pyob && CHECK; }

#define COMMON5( CLASS, BASE, CHECK ) \
    COMMON4( CLASS, BASE, rhsp, CHECK ) \
    CLASS( const Object& ob ) : BASE{ ob.ptr() } { \
        validate(); } \

    // Class Type
    class Type: public Object
    {
    public:
        COMMON5( Type, Object, _Type_Check(pyob) )

        Type( const Type& t ) : Object{t}  { validate(); }
    };

    // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

    class Boolean: public Object
    {
    public:
        COMMON5( Boolean, Object, PyObject_IsTrue(pyob) != -1 )

        Boolean( const Boolean& ob ) : Object{ob}  { validate(); }

        Boolean( bool v=false )       { set( PyBool_FromLong(v?1:0), true ); validate(); }      // create from bool
        Boolean& operator=( bool v )  { set( PyBool_FromLong(v?1:0), true ); return *this; }

        operator bool()         const { return as_bool(); }
    };

    // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

    class Long: public Object
    {
    public:
        COMMON4( Long, Object, PyNumber_Long(rhsp), _Long_Check(pyob) )

        // π I *think* the explicit was originally on the wrong function below
        explicit Long( const Long& ob )                     : Object{ ob }                                               { validate(); }
                 Long( const Object& ob )                   : Object{ PyNumber_Long(ob.ptr())                   , true } { validate(); } // ... any object
        :
    }
    : (etc)

但是,我刚刚发现C ++ 11支持构造函数继承,例如:

Code Demo

#include <string>
#include <iostream>

using namespace std;

class B {
     public:
     B() { cout << "B:-\n"; }
     B(int i) { cout << "B:int\n"; }
     B(float i) { cout << "B:float\n"; }
};

class D : public B {
     using B::B;
     public:
     D(double i) : B() { cout << "D:double\n"; }
};

class D2 : public B {
     using B::B;
     public:
     D2(float i) : B() { cout << "D:float\n"; }
};

class D3 : public B {
     using B::B;
     public:
     D3(float i) = delete;
};

int main() {
    D a{5};    // B:int
    D b{5.f};  // B:float
    D c{5.};   // B:- D:double

    D2 d{5.f}; // B:- D:float

    //D3 e{5.f}; // error: use of deleted function 'D3::D3(float)'
}

此代码显示可以回退到基类构造函数,同时提供覆盖它们的机会。

这正是我所需要的,因为偶尔会有一个类略微打破模式。

这是重写:

using CheckType = bool(PyObject*);
inline PyObject* setDefault(PyObject* pyob){ return pyob; };

template< typename Derived,  typename Base,  CheckType checkfunc,  decltype(setDefault) setfunc = setDefault >
class ObjBase : public Object
{
public:
    ObjBase(){ };

    explicit ObjBase( PyObject* pyob, bool owned=false ) : Base{pyob,owned} { validate(); }

    Derived& operator=( const Object& rhs  )  {                                      return *this = rhs.ptr(); }
    Derived& operator=( PyObject*     rhsp )  { if(ptr()!=rhsp) set(setfunc(rhsp));  return *this; }

    bool accepts( PyObject* pyob )  const override { return pyob && checkfunc(pyob); }

    ObjBase( const Object& ob ) : Base{ob.ptr()}  { validate(); }
};

// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

// Class Type
class Type: public ObjBase< Type, Object, _Type_Check > {
    using          ObjBase< Type, Object, _Type_Check >::ObjBase;
    using          ObjBase< Type, Object, _Type_Check >::operator=;

public:
    // COMMON5( Type, Object, _Type_Check(pyob) )

    Type( const Type& t ) : ObjBase{t}  { validate(); }
};

// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

inline bool bool_check( PyObject* pyob ){ return PyObject_IsTrue(pyob) != -1; }
class Boolean: public ObjBase< Boolean, Object, bool_check > // <-- 1. Candidate is the implicit copy assignment operator
{
    using ObjBase< Boolean, Object, bool_check >::ObjBase;
    using ObjBase< Boolean, Object, bool_check >::operator=;

public:
    // COMMON5( Boolean, Object, PyObject_IsTrue(pyob) != -1 )

    Boolean( const Boolean& ob ) : ObjBase{ob}  { validate(); }

    Boolean( bool v=false )       { set( PyBool_FromLong(v?1:0), true ); validate(); }      // create from bool

    Boolean& operator=( bool v ) { set( PyBool_FromLong(v?1:0), true ); return *this; } // <-- 2. Candidate function

    operator bool()         const { return as_bool(); }
};
:
: (etc)

第一堂课有效 - 类型。但是,当我尝试使用此类时,Boolean类会导致编译器错误。

错误:使用重载运算符&#39; =&#39;是不明确的(操作数类型&#39; Py :: Boolean&#39;和#39; Py :: Long&#39;)

Boolean b{true}; 
Long l{15};
b = l; // <-- ERROR: Use of overloaded operator '=' is ambiguous (with operand types 'Py::Boolean' and 'Py::Long')

错误指定了我在代码中标记的两个候选项:

    // Candidate function
    Boolean& operator=( bool v ) { set( PyBool_FromLong(v?1:0), true ); return *this; }

但是另一个指向类声明本身的行:

// Candidate is the implicit copy assignment operator
class Boolean: public ObjBase< Boolean, Object, bool_check > // <-- ?! 
{ ...

这是我不明白的。

我的第一个猜测是基类中的赋值运算符存在一些冲突。

但是,错误具体是说&#34;候选人是隐含的复制赋值运算符&#34;

查找&#34;隐式复制赋值运算符&#34;,http://en.cppreference.com/w/cpp/language/as_operator

...表示会生成隐式副本分配,但如果满足某些条件则会删除它。

我的猜测是原来的班级符合这些标准。但是将大多数方法移回基类的行为可能意味着现在没有达到这些标准。

但是,我无法看到它。

如何向前推这一个?

编辑:完整的源文件列表here

编辑:我还注意到,通过将operator =重载移回ObjBase,&#39; *这个&#39;现在不正确。用&#39; static_cast(* this)替换它&#39;然后给出了额外的错误。

1 个答案:

答案 0 :(得分:2)

第二个候选者可能是隐式定义的复制赋值运算符,它未在源代码中显示,因此编译器指向类本身。

您的Long类隐式转换为可转换为bool的各种整数类型,并且这些类型也可转换为Boolean,并且这两种类型都可以分配给Boolean,因此编译器在执行赋值之前不知道要转换的内容。

您可以明确转换,因此编译器不需要检查:

b = bool(l);

或等效,但更明确(对于一些读者可能更清楚):

b = static_cast<bool>(l);

您可能希望避免在类型之间进行过多的显式转换,这会让人感到困惑并导致这些问题。要禁用隐式转换并要求显式强制转换来告诉编译器您要执行哪些转换,请转换构造函数explicit,或者转换运算符explicit(或两者)。