什么" ="操作员返回?

时间:2017-01-02 18:24:39

标签: c++

据我所知,C ++从左到右工作。例如,如果我这样做:

std::cout << "output" << "stream";

C ++首先从最左边的东西(std :: cout)开始,然后是&lt;&lt;获取右侧字符串文字的运算符(&#34;输出&#34;)并将其放在运算符左侧的对象(std :: cout)中。然后C ++返回运算符左侧的对象(std :: cout),然后继续代码,另一个&lt;&lt;操作

&#34; =&#34;操作员返回?

如果我这样做

double wage = 5;
double salary = wage = 9999.99;

我认为&#34; =&#34;运算符只返回&#34; =&#34;的左或右操作数。因此,根据我的逻辑,在工资初始化的行中,工资用工资值初始化,然后&#34; =&#34;运营商返回薪水或工资(让我们说薪水),然后它给工资分配9999.99,但工资消失了,它应该保持5的价值。

但当我检查&#34;薪水&#34;的价值时并且&#34;工资&#34;在&#34;薪水&#34;之后初始化,都有一个值9999.99。如果我应用与上面使用的std :: cout相同的逻辑,那么应该只有一个变量,&#34; salary&#34;或者&#34;工资&#34;,值为9999.99

4 个答案:

答案 0 :(得分:17)

赋值运算符从右到左关联,一般来说通过引用返回其左操作数。一般而言,这适用于所有内置类型,我能想到的库类型,以及您希望如何编写赋值运算符。

这意味着

double salary = wage = 9999.99;

完全相同
wage = 9999.99;
double salary = wage;

请注意,在此处的第二行中,salary设置为wage,而不是9999.99。区别在这里并不重要,但在某些情况下可能会有所不同。例如(感谢贾斯汀时间):

double salary = --(wage = 9999.99);
无论如何,

salary显然得到9998.99的值,但重要的是要注意wage也是如此;如果赋值返回了正确的参数,那么wage仍将最终为9999.99,因为临时将在工资分配发生后减少。

正如Ben Voigt在下面的评论中指出的那样,虽然我的答案是正确的,但我使用的问题的例子有点误导。这是因为尽管=令牌在相关代码中出现两次,但double salary = wage = 9999.99实际上并未调用赋值两次,而是调用赋值然后构造一个新的double。这个例子会更好地继续如下:

double salary = 0.0;
salary = wage = 9999.99;

现在我们真正链接赋值运算符,我以前关于优先级和返回的所有注释都适用于此处的第二行代码。

答案 1 :(得分:3)

咨询operator precedence table。赋值运算符double salary = (wage = 9999.99); 是&#34;从右到左&#34;关联。所以表达式有效地评估为:

wage = 9999.99

首先发生require 'active_support/core_ext/numeric/conversions' 12345.to_s(:delimited) # => "12,345" 12345.6789.to_s(:delimited) # => "12,345.6789"

答案 2 :(得分:1)

表达式具有副作用。值(您正在调用&#34; return&#34;)将用于另一个运算符,该表达式是该运算符的操作数。 (即使没有这样的算子,它仍然可以计算)。查找表达式值的过程称为值计算

该值可能包含value category。副作用是发生的任何其他事情,例如更新内存位置或调用函数。

通常,在较大的子表达式中使用该值之前,可能会或可能不会出现副作用。众所周知,后缀 - ++运算符可能在值和副作用之间的时序上有很大差异。

对于赋值表达式,该值为左操作数(值类别&#34;左值&#34;),副作用是左操作数指定的对象更新为保存右侧的值操作数; 值计算之前,副作用是排序,这保证了如果一个更大的表达式使用该值,那么它指定一个已经存储了新值的内存位置。

如果代码是:

salary = wage = 9999.0;

然后运算符关联性规则意味着它是salary = (wage = 9999.0);。内部表达式的值为wage,值类别为lvalue,内部表达式的副作用是名为wage的变量得到9999.0,并且赋值运算符的排序保证了在我们移动到下一个最外层表达式之前,结果已经存储在那里。

然后我们有salary = X,其中X是前一段中描述的值,即它相当于salary = wage;

请注意,您的实际代码是声明,声明中的第一个=符号不是赋值运算符。相反,它是一个语法标记,初始化器即将到来;你的代码与:

相同
double salary { wage = 9999.0 };

答案 3 :(得分:1)

在你的例子中

double wage = 5;
double salary = wage = 9999.99;

... 只有一个作业,即

wage = 9999.99

=的其他实例是初始化语法的一部分。

赋值返回对分配给的对象的引用,在本例中为wage。所以这个例子相当于

double wage = 5;
wage = 9999.99;
double salary = wage;

如果您的示例被重写为使用多个作业,

double wage;
double salary;
wage = 5;
salary = wage = 9999.99;

...然后,赋值运算符是右关联的,这一点很重要。因此,最后一行等同于

salary = (wage = 9999.99);

...括号表达式返回对wage的引用。

标准容器要求用户定义的赋值运算符返回对指定对象的引用。核心语言没有这样的要求,因此可能会使用void。即保证效率,不支持基于副作用的不良代码,以及更简洁的实现,所有这些都很好:

struct S
{
    void operator=( S );    // OK with respect to core language rules.
};

但是,为了deletedefault赋值运算符,运算符的声明必须为其提供引用返回类型:

struct T
{
    auto operator=( T const& ) -> T&    // But `void` won't work here.
        = delete;
};

关于第三个&amp;抓握手,详细,无保证效率和不支持副作用的代码operator=可以用单独的void成员函数表示,甚至可以运营商:

struct U
{
    void assign( U other );

    auto operator=( U const& other )
        -> U&
    {
        assign( other );
        return *this;
    }
};