为什么不能使用非成员函数来重载赋值运算符?

bjs*_*123 30 c++ operator-overloading assignment-operator

赋值运算符可以使用成员函数重载,但不能使用非成员friend函数重载:

class Test
{
    int a;
public:
    Test(int x)
        :a(x)
    {}
    friend Test& operator=(Test &obj1, Test &obj2);
};

Test& operator=(Test &obj1, Test &obj2)//Not implemented fully. just for test.
{
    return obj1;
}
Run Code Online (Sandbox Code Playgroud)

它会导致此错误:

错误C2801:'operator ='必须是非静态成员

为什么friend函数不能用于重载赋值运算符?编译器允许重载其他运算符,例如+=-=使用friend.支持的固有问题/限制是operator=什么?

AnT*_*AnT 36

首先,应该注意的是,这与作为朋友具体实施的操作员无关.它实际上是将副本赋值实现为成员函数或非成员(独立)函数.这个独立功能是否会成为朋友是完全不相关的:它可能是,可能不是,取决于它想要在类中访问什么.

现在,这个问题的答案在D&E书(C++的设计和演变)中给出.原因是编译器总是为类声明/定义成员复制赋值运算符(如果您没有声明自己的成员复制赋值运算符).

如果该语言还允许将复制赋值运算符声明为独立(非成员)函数,则最终可能会出现以下情况:

// Class definition
class SomeClass {
  // No copy-assignment operator declared here
  // so the compiler declares its own implicitly
  ...
};

SomeClass a, b;

void foo() {
  a = b;
  // The code here will use the compiler-declared copy-assignment for `SomeClass`
  // because it doesn't know anything about any other copy-assignment operators
}

// Your standalone assignment operator
SomeClass& operator =(SomeClass& lhs, const SomeClass& rhs);

void bar() {
  a = b;
  // The code here will use your standalone copy-assigment for `SomeClass`
  // and not the compiler-declared one 
}
Run Code Online (Sandbox Code Playgroud)

如上例所示,复制赋值的语义将在转换单元的中间发生变化 - 在声明独立运算符之前,将使用编译器的版本.声明后使用您的版本.程序的行为将根据您放置独立复制赋值运算符的声明的位置而改变.

这被认为是不可接受的危险(并且是),因此C++不允许将复制赋值运算符声明为独立函数.

确实,在您的特定示例中,恰好使用了友元函数,在类定义中很早就声明了运算符(因为这就是朋友的声明方式).因此,在您的情况下,编译器当然会立即知道您的运算符的存在.但是,从C++语言的角度来看,一般问题与朋友函数无关.从C++语言的角度来看,它是关于成员函数与非成员函数,并且由于上述原因,完全禁止非成员重载复制赋值.


Bil*_*eal 29

因为operator=编译器提供的默认值(成员复制一个)总是优先.即你的朋友operator=永远不会被召唤.

编辑:这个答案正在回答

什么是支持=运营商的固有问题/限制?

部分问题.这里的其他答案引用了标准部分,表示你不能这样做,但这很可能是为什么标准的那部分是这样编写的.

  • 对不起,但这很简单,没有任何意义.为什么编译器的运算符优先?对于**可以*声明为独立函数的运算符,声明成员版本和独立版本会导致*歧义*,而不是成员函数"优先".那个答案背后的陈述背后的逻辑是什么? (6认同)
  • @AndreyT和@Billy Oneal:你们两个都是对的,在不同的背景下.如果赋值是在类方法中执行的,由于查找规则,成员函数(在这种情况下由编译器生成)将优先并隐藏命名空间范围`operator =`.如果赋值发生在类范围之外,则会出现歧义,编译器将失败.虽然无法使用`operator =`进行测试,但使用`operator + =`(或任何其他可以作为成员和自由函数实现的运算符)生成测试相当简单 (4认同)

Chu*_*dad 8

$ 13.5.3 - "赋值运算符应由具有一个参数的非静态成员函数实现.因为如果未由用户声明(12.8),则为类隐式声明了复制赋值运算符operator =,基类赋值运算符总是被派生类的副本赋值运算符隐藏."


Arm*_*yan 7

因为有些运营商必须是会员.这些运营商是:
operator[]
operator=
operator()
operator->

和类型转换运算符一样operator int.

虽然有人可能能够解释为什么operator =必须是成员,但他们的论证不能适用于列表中的其他人,这使我相信"​​为什么"的答案是"仅仅因为".

HTH