为什么不能使用非成员函数来重载赋值操作符?
赋值运算符可以使用成员函数进行重载,但不能使用非成员函数:
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; }
它导致这个错误:
错误C2801:'operator ='必须是非静态成员
为什么不能使用friend
函数来重载赋值操作符? 编译器允许使用friend
来重载其他运算符,例如+=
和-=
。 支持operator=
的固有问题/限制是什么?
因为编译器提供的默认operator=
(成员复制)始终优先。 即你的朋友operator=
永远不会被调用。
编辑:这个答案正在回答
支持=运营商的内在问题/限制是什么?
问题的一部分。 这里的其他答案引用了标准中说你不能这样做的部分,但这很可能是为什么标准的那部分是这样写的。
首先,应该指出,这与运营商作为朋友具体实施无关。 这实际上是将复制分配作为成员函数或作为非成员(独立)函数来实现的。 这个独立的function是否会成为朋友是完全不相干的:它可能不是,它可能不是,取决于class级内部想要访问什么。
现在,这个问题的答案在D&E书( The Design and Evolution of 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 }
如上例所示,复制分配的语义将在翻译单元的中间发生变化 – 在独立运算符的声明之前使用编译器的版本。 声明后你的版本被使用。 程序的行为将会根据您放置独立复制分配操作符声明的位置而改变。
这被认为是不可接受的(而且是),所以C ++不允许将复制赋值运算符声明为独立函数。
确实,在你的特定例子中,恰好使用了一个朋友函数,运算符在类定义内部很早被声明(因为这就是朋友声明的方式)。 所以,在你的情况下,编译器当然会马上知道你的操作符的存在。 但是,从C ++语言的angular度来看,一般问题与朋友function无关。 从C ++语言的angular度来看,它是关于成员函数与非成员函数的关系,并且仅仅因为上述原因而禁止了复制分配的非成员重载。
$ 13.5.3 – “一个赋值运算符应该由一个非静态成员函数来实现,它只有一个参数, 因为如果用户没有声明一个赋值运算符operator =被隐式地声明为一个类(12.8),那么基类赋值运算符总是被派生类的复制赋值运算符隐藏“。
因为有一些运营商必须是会员。 这些运营商是:
operator[]
operator=
operator()
operator->
和types转换运算符(如operator int
。
虽然人们可以解释为什么operator =必须是成员,但是他们的论点不能适用于列表中的其他人,这使我相信“为什么”的答案是“仅仅是因为”。
HTH
operator=
是一个特殊的成员函数,如果你不自己声明的话,编译器会提供这个成员函数。 由于operator=
的这种特殊的地位operator=
它有意义的要求它是一个成员函数,所以不可能存在一个编译器生成的成员operator=
和一个用户声明的友好operator=
并且不可能select他们俩。
为什么friend函数不能用于重载赋值运算符?
简短的回答: 只是因为 。
稍微长一些的答案:这是语法修复的方式。 一些操作员必须是成员函数 。 赋值运算符是其中之一,
operator=
的意图是对当前对象的赋值操作。 那么LHS或Lvalue是同一types的对象。
考虑一个LHS是整数或其他types的情况。 这是由operator int()
或相应的operator T()
函数处理的情况。 因此,LHS的types已经被定义,但是一个非成员operator=
函数可能违反了这个。
因此避免。
因为在类中已经有一个隐式的运算符重载函数来执行浅拷贝 。 所以,即使你使用aa函数重载, 你也永远无法调用它,因为我们所做的任何调用都会调用隐式浅拷贝方法,而不是重载的friend函数。
这篇文章适用于C ++ 11
为什么有人想要一个非会员operator=
? 那么,与一个成员operator=
然后下面的代码是可能的:
Test const &ref = ( Test() = something );
这创造了一个悬而未决的参考。 非会员运营商可以解决这个问题:
Test& operator=(Test &obj1, Test obj2)
因为现在prvalue Test()
将无法绑定到obj1
。 事实上,这个签名会强制我们永远不会返回一个悬而未决的引用(当然,除非我们提供了一个引用),函数总是返回一个“有效的”左值,因为它强制使用左值调用。
然而,在C ++ 11中,现在有一种方法可以指定一个成员函数只能在左值被调用,所以你可以通过编写成员函数来实现相同的目标:
Test &operator=(Test obj2) & // ^^^
现在上面的悬挂引用代码将无法编译。
NB。 operator=
应该通过value或const引用来获取右侧。 在实施复制和交换习惯用法时 ,采用价值是很有用的,这种技巧可以方便地编写安全的(但不一定是最快的)复制分配和移动赋值操作符。