需要为右值分配哪些向后兼容性?

时间:2018-07-18 06:52:36

标签: c++

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;。 (由于关系密切,我选择不公开其他问题)

2 个答案:

答案 0 :(得分:2)

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

  1. 这样的限制不会破坏任何特定的代码,但是会限制可接受程序的域。在进行此类更改时,c ++相当保守,有时会非常痛苦。一个值得注意的例子是最令人烦恼的解析,如果将A a();解释为默认构造的A,将会破坏什么代码?但是,这种方法无法与c语法向后兼容。这是用于语法分析的相当好的PIA。

  2. c ++允许对用户定义类型的运算符进行语义重新定义。我不知道是否有针对operator=的语义重新定义的好例子,但是 boost::program_options "abuses" operator() in a quite weird way, to create a concise syntaxEigen redefines comma operator semantics。指针到成员运算符通常被重新定义为执行非标准操作,因为默认情况下不经常使用它们。所以有时候它很有用。

  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;
}

它将...破裂。这是因为按位移位的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 +产生副作用对于像我所展示的操作是敞开的大门。