【问题标题】:Scope and assignment to return values返回值的范围和赋值
【发布时间】:2016-10-31 04:36:27
【问题描述】:
 1 class Foo{
 2 
 3 };
 4 
 5 Foo func1(){
 6   Foo f;
 7   return f;
 8 }
 9 
10 int func2(){
11   int a = 2;
12   return a;
13 }
14 
15 int main(int argc, char** argv){
16   Foo var1;
17   func1() = var1; //OK
18   func2() = 1; //error: expression is not assignable
19   return 0;
20 }

不允许赋值给内置类型的返回值但允许赋值给用户定义类型的返回值的根本原因是什么?如何管理允许一个但不允许另一个的内存?

【问题讨论】:

  • 你认为func1() = var1;这意味着什么?
  • 我理解它调用Foo类的赋值运算符,我的问题是内置类型和用户定义类型在这件事上的根本区别是什么? Foo& 运算符=(Foo&);
  • .. 然后呢?
  • 复制赋值运算符创建一个源的副本,并为每个数据成员制作一个成员副本。我明白为什么这个有效。为什么它不适用于内置类型?
  • 然后丢弃结果。 IE。实际上什么都不做

标签: c++ c++11


【解决方案1】:

看看this article关于价值类别。

价值类别

每个 C++ 表达式(带有操作数的运算符、文字、变量名等)都有两个独立的属性:类型和值类别。 [...]

prvalue

以下表达式是纯右值表达式:

[...]

  • 函数调用或非引用返回类型的重载运算符表达式,例如 str.substr(1, 2)

好的,所以我们知道表达式func1()func2() 是纯右值。

[...]

右值

右值表达式是纯右值或 xvalue。

现在我们知道纯右值是右值。

属性:

[...]

右值不能用作内置赋值或复合赋值运算符的左操作数。

[...]

这是重要的部分。请注意,该规则仅排除内置分配。仍然允许复制类类型的赋值运算符。

因此,内置运算符和类类型运算符之间的区别在于语言规则对它们的处理方式不同。


现在,您可能想知道,为什么内置类型不允许赋值?好吧,它没有用例。赋值没有副作用(除了改变左侧操作数的值)并且返回值被丢弃。如果你写它,你很可能犯了一个错误。禁止它对程序员很有帮助。

现在,您可能想知道,分配给右值类类型不是也没有用吗?好吧,可能不是!类类型可以具有用户定义的具有副作用的赋值运算符。并且类类型可以具有具有副作用的析构函数,具体取决于先前执行的分配。我不能保证这是 Bjarne 或委员会在指定语言时使用的推理,但这是我对此事的看法。

【讨论】:

  • 也许自动合成的赋值运算符应该是operator=(const T&) &;,以便它只适用于左值(注意后面的&)。这更直观,也许符合 OP 的直觉。你为什么要分配到一个临时的?如果开发人员想要“强制”编译器允许分配到右值,他们可以写operator(const T&) && = default;
  • @AaronMcDaid 好吧,赋值在 c++11 之前就已经存在,但引用限定符还没有,所以这不可能是最初指定隐式复制赋值的方式。既然 ref-qualifiers 存在,这种改变是可能的,它的采用将取决于委员会工作组是否认为这种改变值得他们花时间——假设这种改变首先是正式提出的。
  • 我可以看到的缺点是您可能希望使用一个模板来为具有自定义重载的类型执行赋值魔术,并默默地优化具有隐式运算符的类型的赋值。您提议的更改将与此类模板向后不兼容。
猜你喜欢
  • 2013-04-08
  • 1970-01-01
  • 2017-07-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-24
  • 2011-05-20
  • 1970-01-01
相关资源
最近更新 更多