问题
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;
}
};
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;
}
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?
回答1:
§13.3.1.2 [over.match.oper]/p3 (emphasis added):
for a binary operator
@
with a left operand of a type whose cv-unqualified version isT1
and a right operand of a type whose cv-unqualified version isT2
, three sets of candidate functions, designated member candidates, nonmember candidates and built-in candidates, are constructed as follows:
- [...]
- 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 typeT1
or “reference to (possibly cv-qualified) T1”, whenT1
is an enumeration type, or (if there is a right operand) a second parameter of typeT2
or “reference to (possibly cv-qualified)T2
”, whenT2
is an enumeration type, are candidate functions.- [...]
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.
来源:https://stackoverflow.com/questions/27361230/implicit-conversion-of-lefthand-argument-in-in-class-declared-friend-operator