类内朋友运营商似乎没有参与重载决议

iFr*_*cht 5 c++ overloading operator-overloading friend

在编写一个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;
    }
};
Run Code Online (Sandbox Code Playgroud)

看起来有点过分,但是必要,因为它Foo my = FooValueT::one + FooValueT::zero;应该是一个有效的表达式,如果没有任何参数具有class-type,它们就不会被隐式转换,正如我在上一个问题的答案中所解释的那样.

尽管付出了所有这些努力,但以下代码无法编译:

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;
}
Run Code Online (Sandbox Code Playgroud)

错误消息是:

error C2676: binary '+' : 'FooValueT' does not define this operator or a conversion to a type acceptable to the predefined operator
Run Code Online (Sandbox Code Playgroud)

一旦我将操作符完全移出类,或将其声明为朋友但在类外定义它,此问题就会解决.当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;
}
Run Code Online (Sandbox Code Playgroud)

我在哪里错了?与普通的免费朋友功能相比,功能的同类朋友定义是否会改变重载决策的规则?

For*_*veR 5

n3376 11.3/6-7

当且仅当该类是非本地类(9.8),函数名称是非限定的,并且该函数具有命名空间范围时,才能在类的友元声明中定义函数.

这样的功能是隐含的内联.在类中定义的友元函数位于定义它的类的(词法)范围内.在类外定义的友元函数不是(3.4.1).

在你的情况下,运算符在类范围内,当你试图调用这个运算符时,ADL不会尝试在类中找到运算符,因为这两个参数都没有这个类的类型.只需编写自由函数(或不在课堂上声明的朋友).

好像你不能做这样的事情,类中的友元函数声明的问题是函数会在类范围内,但是你不能将这个函数声明为free friend函数,因为编译器不能推断return-type参数.

  • 7.3.1.2/3是更相关的引用. (2认同)