在编写一个CRTP模板时,它允许类根据模板参数为operator+
提供重载,我发现一个类内的友元操作符似乎没有参与重载解析,如果它的参数都不是它所定义的类的类型。
归结为:
enum class FooValueT{
zero, one, two
};
class Foo{
FooValueT val_;
public:
Foo(FooValueT x) : val_(x){};
Foo& operator+=(Foo other){
val_ = (FooValueT)((int)val_ + (int)other.val_);
return *this;
}
//overload for Foo+Foo, FooValueT+Foo and Foo+FooValueT
friend Foo operator+(Foo lhs, Foo rhs){
Foo ret = lhs;
return ret += lhs;
}
//explicit overload for FooValueT+FooValueT
friend Foo operator+(FooValueT lhs, FooValueT rhs){
return (Foo)lhs + (Foo)rhs;
}
};
看起来有点过分,但是必要,因为Foo my = FooValueT::one + FooValueT::zero;
应该是一个有效的表达式,如果没有任何参数具有class-type,它们不会被隐式转换,如this answer中对前一个所述我的问题。
尽管付出了这些努力,但以下代码仍无法编译:
int main(int argc, char* argv[])
{
Foo my = FooValueT::zero;
my += FooValueT::one;
my = Foo(FooValueT::zero) + FooValueT::two;
my = FooValueT::zero + Foo(FooValueT::two);
my = FooValueT::zero + FooValueT::two; //error C2676
return 0;
}
错误消息是:
error C2676: binary '+' : 'FooValueT' does not define this operator or a conversion to a type acceptable to the predefined operator
一旦我将运算符完全移出类,或将其声明为朋友但在类外定义它,此问题就会解决。当Foo
是要派生自的模板类时,两者似乎都不可行。
据我所知,operator+(ValueT,ValueT)
的上述朋友定义应该创建一个自由函数,就像这个定义一样:
class Foo{
/*All the stuff you saw previously*/
friend Foo operator+(FooValueT lhs, FooValueT rhs);
};
Foo operator+(FooValueT lhs, FooValueT rhs){
return (Foo)lhs + (Foo)rhs;
}
我在哪里错了?与常规的免费朋友函数相比,函数的类内朋友定义是否会改变重载决策的规则?
答案 0 :(得分:5)
n3376 11.3 / 6-7
如果和,可以在类的朋友声明中定义函数 只有当类是非本地类(9.8)时,函数名才是 不合格,函数具有命名空间范围。
这样的功能是隐式内联的。 在。中定义的朋友功能 class位于定义它的类的(词法)范围内。一个 在课外定义的朋友函数不是(3.4.1)。
在你的情况下,运算符在类范围内,当你试图调用这个运算符时,ADL不会尝试在类中找到运算符,因为这两个参数都没有这个类的类型。只需编写自由函数(或不在课堂上声明的朋友)。
好像你不能做这样的事情,类中的友元函数声明的问题是函数会在类范围内,但你不能声明这个函数是免费的朋友函数,因为编译器不能推断返回类型参数