右值需要分配什么向后兼容性

What backward compatibility needs assignment for rvalues?

本文关键字:兼容性 什么 分配      更新时间:2023-10-16

C++初级读本第五版:

(代码也来自书中,此处提供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;是不允许的。(由于关系密切,我选择不回答另一个问题(

这可能是一个反气候的猜测。我欢迎任何其他具体的例子,但遵循一般规则似乎是非常合理的。

  1. 这样的限制不会破坏任何特定的代码,但会限制可接受程序的域。当涉及到这样的更改时,c++相当保守,有时会非常痛苦。一个值得注意的例子是最麻烦的解析,如果A a();被解释为默认构造的A,什么代码会中断?然而,它不能以这种方式向后兼容c语法。这是语法分析的PIA。

  2. c++允许对用户定义类型的运算符含义进行语义重新定义我不知道是否有一个很好的例子可以对operator=进行语义重新定义,但对于其他运算符,也有一些值得注意的例子boost::program_options以一种非常奇怪的方式"滥用"operator(),以创建一个简洁的语法,Eigen重新定义了逗号运算符语义。指向成员运算符的指针通常被重新定义为执行非标准操作,因为默认情况下它们不经常使用。所以有时候它很有用。

  3. 我想它可能对operator=有副作用的类有用,并且不一定意味着要更改内存中的值。我想在一些嵌入式开发中,您可以使用RowCol,然后编写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;
}

它将。。。打破这是因为逐位移位优先于=。它需要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=()也是如此。

好处:ints的示例不起作用,因为不能重载基元类型的运算符,并且定义了默认运算符,因此它不接受右值。它展示了你所期望的行为。基本上,我们被剥夺了重新定义基元类型运算符语义的自由。

这里有一个当前完全有效的代码示例(甚至不是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 +产生副作用是为我所展示的操作打开的大门。