C ++ Primer 5th :
(代码也来自本书,此处提供了99%的上下文)
#include <string>
using namespace std;
int main()
{
//no error
string s1 = "123", s2 = "aaaa";
s1 + s2 = "wow";
auto a = (s1 + s2).find("a");
}
在新标准(这里是C ++ 11)之前,没有任何方法可以防止这种用法。在 为了保持向后兼容性,库类继续 允许分配给右值,但是,我们可能要防止这种情况 在我们自己的班级中的用法。在这种情况下,我们想强制 左侧操作数(即
this
指向的对象)是 左值。
需要为右值分配什么向后兼容性?
顺便说一句,我也很好奇为什么允许s1 + s2 = "wow"
但不允许int i = 3, j = 4; i + j = 7;
。 (由于关系密切,我选择不公开其他问题)
答案 0 :(得分:2)
这可能是反气候知识的猜测。我将欢迎任何其他具体示例,但是遵循一般规则似乎非常合理。
这样的限制不会破坏任何特定的代码,但是会限制可接受程序的域。在进行此类更改时,c ++相当保守,有时会非常痛苦。一个值得注意的例子是最令人烦恼的解析,如果将A a();
解释为默认构造的A
,将会破坏什么代码?但是,这种方法无法与c语法向后兼容。这是用于语法分析的相当好的PIA。
c ++允许对用户定义类型的运算符进行语义重新定义。我不知道是否有针对operator=
的语义重新定义的好例子,但是有 boost::program_options "abuses" operator()
in a quite weird way, to create a concise syntax和Eigen redefines comma operator semantics。指针到成员运算符通常被重新定义为执行非标准操作,因为默认情况下不经常使用它们。所以有时候它很有用。
我猜想对于operator=
有副作用的类可能有用,并且不一定意味着更改内存中的值。我想象在某些嵌入式开发中,您可以拥有Row
和Col
并编写row * col = LED_ON
或类似内容。我脑海中的另一个例子是表达式库,例如,尚无operator<=>
,因此可以在此处使用operator=
来评估诸如(p ^ p) <=> p
之类的东西。
operator=
在运算符之间没有特殊之处,并且与其他成员函数相比确实没有什么特殊之处。如果您编写这样的代码:
#include <iostream>
using namespace std;
struct A{
friend ostream& operator<<(ostream& out, A& a) { out << "A "; return out;}
};
int main() {
A a1, a2;
cout << a1=a2 << '\n';
return 0;
}
它将...破裂。这是因为按位移位的precedence比=
大。它需要在a1=a2
周围加上括号。这是为了说明operator=
确实对该语言没有特殊权利。
另一件事是,您可以根据需要重载那些运算符,因此编写
#include <iostream>
using namespace std;
struct A{
bool operator=(A& rhs) {return true;}
};
int main() {
A a1, a2;
cout << (a1=a2) << '\n';
return 0;
}
完全合法。语言为操作员提供了语法快捷方式,仅此而已。我不认为很多人抱怨(a+b).method()
有用,(a+b).operator=()
也是一样。
奖金:带有int
的示例不起作用,因为您不能重载原始类型的运算符,并且定义了默认值,因此它不接受右值。它表现出您似乎期望的行为。基本上,我们摆脱了重新定义原始类型运算符的语义的自由。
答案 1 :(得分:1)
这是当前完全有效的代码示例(甚至没有Clang警告),并且该代码可能会中断。诀窍是operator +
返回一个具有重新定义的operator =
的特殊对象。
在示例中,重新定义的operator =
设置了创建特殊对象的加法运算的第一个操作数:
#include <iostream>
// A simple class containing an int with a special operator =
class A {
public:
int val;
class B operator + (const A& other);
};
/* A very special subclass keeping a ref on the first member
* of the addition of A objects that created it. This ref
* will be assigned by operator = */
class B: public A {
A& ref; // ref on the A object used at creation time
// private ctor: B can only be created from A objects sums
B(A& orig): ref(orig) {
this->val = orig.val;
}
public:
B(const B& src): A(src), ref(src.ref) {} // copy ctor...
// second part of the trick: operator = assigns ref
B& operator = (const A& src) {
ref = src;
return *this;
}
friend class A;
};
B A::operator +(const A& other) {
B ret(*this);
ret.val += other.val;
return ret;
}
int main(){
A a = {1}, b= {2}, c= {5};
A d;
d = a + b = c;
// a+b will eval to a B having val=3 and ref=a
// a+b = c will set a.val to 5 AFTER ABOVE EVALUATION
// d = a+b will set d.val to 3
std::cout << a.val << " " << b.val << " " << c.val << " " << d.val << std::endl;
// outputs as expected: 5 2 5 3
return 0;
}
好吧,我无法想象一个真正的用例,但是如果编译器接受它们,程序员可能会有奇怪的想法(毕竟 I 可以想象...)。而且我真的认为让operator +
产生副作用对于像我所展示的操作是敞开的大门。