在类中声明的朋友操作符的左手参数的隐式转换 [英] Implicit conversion of lefthand argument in in-class declared friend operator

查看:157
本文介绍了在类中声明的朋友操作符的左手参数的隐式转换的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用CRTP为类提供模板参数相关的函数添加,在这种情况下添加 operator + operator + = ,使用模板类 ImplAdd 。对于前者,应该对两个参数执行隐式转换,这意味着我必须使用类中的friend运算符,如下所示:

I am using CRTP to provide template-argument dependent addition of functions to a class, in this case the addition of operator + and operator +=, using the template class ImplAdd. For the former, implicit conversions should be performed on both arguments, which means I have to use an in-class friend operator like this:

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 为这些常量定义常量值和唯一值类型,很像一个范围化的枚举。

This is needed because classes that actually inherit from ImplAdd define constant values and a unique value type for those constants, much like a scoped enum.

//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){}
};

从我的角度来看,下面的代码现在很容易编译,但它不能。 >

From my perspective, the following code should now easily compile, but it doesn't.

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的行,将打印以下错误消息: p>

For both lines marked with C2676, the following error message is printed:

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

我做错了什么?不是使用定义一个操作符作为类的朋友通常的方式来启用隐式转换两个参数?

What am I doing wrong? Isn't the use of defining an operator as an in-class friend the common way to enable implicit conversion on both arguments? If not, how can I do it in this case?

推荐答案

§13.3.1.2[over.match.oper] / p3 (强调添加):

§13.3.1.2 [over.match.oper]/p3 (emphasis added):


用于二进制运算符 @
cv-unqualified版本为 T1 的类型以及
cv-unqualified版本为 T2的类型的右操作数,三组候选函数,
指定成员候选非成员候选内置
候选
的结构如下:

for a binary operator @ with a left operand of a type whose cv-unqualified version is T1 and a right operand of a type whose cv-unqualified version is T2, three sets of candidate functions, designated member candidates, nonmember candidates and built-in candidates, are constructed as follows:


  • [...]

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

  • / li>
  • [...]
  • The set of non-member candidates is the result of the unqualified lookup of operator@ in the context of the expression according to the usual rules for name lookup in unqualified function calls (3.4.2) except that all member functions are ignored. However, if no operand has a class type, only those non-member functions in the lookup set that have a first parameter of type T1 or "reference to (possibly cv-qualified) T1", when T1 is an enumeration type, or (if there is a right operand) a second parameter of type T2 or "reference to (possibly cv-qualified) T2", when T2 is an enumeration type, are candidate functions.
  • [...]

在简单的英语中,如果两个操作数都没有类类型,对于要考虑的重载的枚举操作数,这就是为什么 my = MyEnum :: zero + MyEnum :: two; 不工作。 my = MyEnum :: zero + MyEnum(MyEnum :: two); ,奇怪的是,在GCC中编译,但不是Clang。我找不到任何使它不合法,所以我怀疑这可能是一个编译器错误。

In plainer English, if neither operand has a class type, then you need to have an exact match on an enumeration operand for the overload to be considered, which is why my = MyEnum::zero + MyEnum::two; does not work. my = MyEnum::zero + MyEnum(MyEnum::two);, oddly enough, compiles in GCC but not Clang. I can't find anything that makes it not legal, so I'm suspecting that this might be a compiler bug.

这篇关于在类中声明的朋友操作符的左手参数的隐式转换的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆