可以使用成员函数重载赋值运算符,但不能使用非成员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;
}
导致此错误:
错误C2801:'operator ='必须是非静态成员
为什么friend
函数不能用于重载赋值运算符?编译器允许使用+=
重载其他运算符,例如-=
和friend
。支持operator=
的固有问题/限制是什么?
答案 0 :(得分:34)
首先,应该注意的是,这与作为朋友实施的运营商无关。它实际上是将副本赋值实现为成员函数或非成员(独立)函数。这个独立功能是否会成为朋友是完全不相关的:它可能是,也可能不是,取决于它想要在课堂内访问的内容。
现在,这个问题的答案在D& E book(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 ++不允许将复制赋值运算符声明为独立函数。
确实,在您的特定示例中,恰好使用 friend 函数,在类定义中很早就声明了运算符(因为这就是朋友的声明方式)。因此,在您的情况下,编译器当然会立即知道您的运算符的存在。但是,从C ++语言的角度来看,一般问题与朋友函数无关。从C ++语言的角度来看,它是关于成员函数与非成员函数,并且由于上述原因,完全禁止非成员重载复制赋值。
答案 1 :(得分:27)
因为编译器提供的默认operator=
(成员复制一个)总是优先。即你的朋友operator=
永远不会被召唤。
编辑:这个答案正在回答
支持=运营商的固有问题/限制是什么?
问题的一部分。这里的其他答案引用了标准部分,表示你不能这样做,但这很可能为什么标准的那部分就是这样编写的。
答案 2 :(得分:8)
$ 13.5.3 - “赋值运算符应由具有一个参数的非静态成员函数实现。因为如果未由用户声明,则为类隐式声明复制赋值运算符operator =(12.8 ),基类赋值运算符总是被派生类的复制赋值运算符隐藏。“
答案 3 :(得分:7)
因为有些运营商必须是会员。这些运营商是:
operator[]
operator=
operator()
operator->
和类型转换运算符,例如operator int
。
虽然有人可能能够解释为什么operator =必须是成员,但他们的论点不能适用于列表中的其他人,这使我相信“为什么”的答案是“仅仅因为”。
HTH
答案 4 :(得分:3)
operator=
是一个特殊的成员函数,如果您不自己声明,编译器将提供该函数。由于operator=
的这种特殊状态,因此ro需要它成为成员函数,因此不可能存在编译器生成的成员operator=
和用户声明的朋友{{ 1}}并且不可能在两者之间进行选择。
答案 5 :(得分:1)
为什么友元函数不能用于重载赋值运算符?
简短回答: 仅仅因为 。
更长的答案:这就是语法修复的方式。一些运营商 必须是会员功能 。赋值运算符是
之一答案 6 :(得分:0)
operator=
的意图是对当前对象的赋值操作。然后LHS或左值是一个相同类型的对象。
考虑LHS是整数或其他类型的情况。这是由operator int()
或相应的operator T()
函数处理的案例。因此,LHS的类型已经定义,但非成员operator=
函数可能违反了这一点。
因此可以避免。
答案 7 :(得分:0)
此帖子适用于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=
应该通过值或const引用来获取右侧。在实现copy and swap idiom时,按值取值很有用,这是一种轻松编写安全(但不一定是最快)的复制赋值和移动赋值运算符的技术。
答案 8 :(得分:0)
因为已经为类中的' =' 执行了浅层复制的隐式运算符重载功能。所以,即使你使用朋友函数重载你也永远无法调用它,因为我们所做的任何调用都会调用隐式浅复制方法而不是重载的友元函数。