【问题标题】:Function returning struct as LValue函数将结构返回为 LValue
【发布时间】:2016-09-29 09:37:48
【问题描述】:

在下面的sn-p中,为什么o.margin() = m;行编译没有错误?它很容易值得警告,因为它几乎总是一个错误。我实际上会认为这是一个错误,因为它会将 R 值放在赋值的左侧。

#include <iostream>

struct Margin
{
    Margin(int val=0) : val(val) {};
    int val;
};

struct Option
{
    Margin m;
    int z=0;

    Margin margin()const { return m; }
    int zoomLevel() { return z; }
};


int main()
{
    Option o;
    std::cout << "Margin is: "<< o.margin().val << std::endl;

    Margin m = { 3 };

    // The following line is a no-op, which generates no warning:
    o.margin() = m;

    // The following line is an error
    // GCC 4.9.0: error: lvalue required as left operand of assignment
    // clang 3.8: error: expression is not assignable
    // MSVC 2015: error C2106: '=': left operand must be l-value
     o.zoomLevel() = 2;

    std::cout << "Margin is: "<< o.margin().val << std::endl;

    return 0;
}

输出:

Margin is: 0
Margin is: 0

【问题讨论】:

  • "为什么它不是警告" 因为它不是。你的实际问题是什么?
  • @king_nak 语言律师标签真的没必要。这是一个 QOI 问题,而不是语言问题。
  • 我不熟悉标签或 QOI,但我的意思是它应该是“至少是一个警告”。我会认为它实际上是一个错误。
  • 我认为,QOI 的意思是“实施质量”。这正是这件事的意义所在。尽管标准在技术上允许它(使 [language-lawyer] 不适用),但您想要编写这样的代码的原因几乎没有,因此质量实现可以说应该发出警告。很像条件表达式中允许赋值的方式,但 99% 的情况下,这不是您的本意,因此编译器现在在您编写 if (foo = 42) 时会发出警告

标签: c++ gcc


【解决方案1】:

如果你想禁止这样的使用,从 C++11 开始你可以在赋值运算符上使用reference qualifier

Margin& operator=(const Margin&) & = default;

这将在 GCC 5.1 上产生以下错误:

error: passing 'Margin' as 'this' argument discards qualifiers [-fpermissive]

您可能还想查看this related question

【讨论】:

  • 这很有趣。新的 c++ 信条似乎是:如果有疑问,请添加与号。恐怕 99.9% 的 c++ 用户都不知道那行的意思。 -Warn-on-rvalue-assignment 标志会更有用。
【解决方案2】:

您可以修改类类型的返回类型(通过在其上调用非 const 方法):

3.10/5 来自 n4140

5 对象的左值是修改对象所必需的 除了类类型的右值也可以用来修改它的 特定情况下的参照物。 [ 示例:成员函数 调用对象(9.3)可以修改对象。 ——结束示例]

你的代码:

o.margin() = m;

其实和

是一样的
o.margin().operator=( Margin(m) );

所以调用非 const 方法,如果你把它改成:

o.margin().val = m;

那么你会得到一个错误。

另一方面:

o.zoomLevel() = 2;

zoomLevel()返回非类类型,所以不能修改。

【讨论】:

  • 我接受这个答案,因为它引用了标准。不过,我仍然感到惊讶,没有主要编译器发出警告。
  • @bgp2000 没有警告是因为标准允许它并且有使用此功能的成语,例如:named-parameter-idiom : isocpp.org/wiki/faq/ctors#named-parameter-idiom
  • 我从不怀疑可以在右值上调用成员函数。我只是从未想过它会与赋值运算符一起使用。你知道使用赋值运算符的成语吗?
  • 不是,但它也是一个函数
【解决方案3】:

Option::margin() 是一个 const-可访问 成员函数,它返回一个可变的 Margin 对象。

因此,分配临时是有效的,因为在 Margin 上使用 operator= 是有效的。在这种情况下,它没有副作用,基本上什么都不做。 C++ 编译器的特定实现可能选择实现语义分析并警告您,但这完全超出了该语言的范围。

【讨论】:

    【解决方案4】:

    o 是类类型的对象时,operator= 是成员函数。代码o.margin() = m; 等价于o.margin().operator=(m);

    您可以调用临时类对象的成员函数,类似于访问o.margin().val 中的成员的方式。

    此外,类的赋值运算符可以被覆盖,并且根本不是空操作。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-02-29
      • 2015-02-14
      • 2019-06-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多