在类内声明的友元运算符中隐式转换左手参数

时间:2014-12-08 15:27:05

标签: c++ c++11 operator-overloading friend crtp

我正在使用CRTP为类提供依赖于模板参数的函数添加,在这种情况下,使用模板类operator +添加operator +=ImplAdd。对于前者,应该对两个参数执行隐式转换,这意味着我必须使用这样的类内友好运算符:

template<class Type, bool active>
struct ImplAdd{
    virtual int get_val_() const = 0;
    virtual void set_val_(int) = 0;
};

//if activated is true, the operators + and += will be defined
template<class Type>
class ImplAdd < Type, true > {
    virtual int get_val_() const = 0;
    virtual void set_val_(int) = 0;
    Type* this_(){ return (Type*)this; }
public:
    Type& operator +=(const Type& x){
        set_val_(get_val_() + x.get_val_());
        return *this_();
    }

    //This should enable conversions on the lefthand argument
    friend Type& operator+(const Type& lhs, const Type& rhs){
        Type ret = lhs;
        return ret += rhs;
    }
};

这是必需的,因为实际从ImplAdd继承的类为这些常量定义常量值和唯一值类型,就像作用域枚举一样。

//by using true as the template argument, the operators + and += will be defined
class MyEnum : public ImplAdd<MyEnum, true>{
    int get_val_() const override{
        return (int)value;
    }
    void set_val_(int v) override{
        value = (ValueT)v;
    }
public:
    enum class ValueT{
        zero, one, two
    };
private:
    typedef  int UnderlyingT;
    ValueT value;
public:
    static const ValueT zero = ValueT::zero;
    static const ValueT one = ValueT::one;
    static const ValueT two = ValueT::two;
    MyEnum(ValueT x) : value(x){}
    MyEnum(const MyEnum& other) : value(other.value){}
};

从我的角度来看,现在应该可以轻松编译以下代码,但事实并非如此。

int main(int argc, char* argv[])
{
    MyEnum my = MyEnum::zero;                //works
    my += MyEnum::one;                       //works
    my = MyEnum(MyEnum::zero) + MyEnum::two; //works
    my = MyEnum::zero + MyEnum(MyEnum::two); //ERROR C2676
    my = MyEnum::zero + MyEnum::two;         //ERROR C2676
    MyEnum my2 = my + my;                    //works
    return 0;
}

对于标有C2676的两行,都会打印以下错误消息:

error C2676: binary '+' : 'const MyEnum::ValueT' does not define this operator or a conversion to a type acceptable to the predefined operator

我做错了什么?是不是使用将运算符定义为类内朋友的方法来启用两个参数的隐式转换?如果没有,在这种情况下我该怎么做?

1 个答案:

答案 0 :(得分:3)

§13.3.1.2[over.match.oper] / p3(强调补充):

  

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

     
      
  • [...]
  •   
  • 非成员候选者集合是根据表达式在表达式上下文中operator@的非限定查找的结果。   非限定函数调用中通常的名称查找规则(3.4.2)   除了忽略所有成员函数。 但是,如果没有操作数   具有类类型,只有查找集中的非成员函数   具有类型T1的第一个参数或“可能的参考”   cv-qualified)T1“,T1是枚举类型,或者(如果有)   右操作数)类型T2或“引用”的第二个参数   (可能是cv-qualified)T2“,当T2是枚举类型时,是   候选人职能。
  •   
  • [...]
  •   

在简洁的英语中,如果两个操作数都没有类类型,那么您需要在枚举操作数上进行精确匹配才能考虑重载,这就是my = MyEnum::zero + MyEnum::two;不起作用的原因。奇怪的是,my = MyEnum::zero + MyEnum(MyEnum::two);在海湾合作委员会编制,但不是在克朗。我找不到任何使它不合法的东西,所以我怀疑这可能是一个编译器错误。