【问题标题】:Are return types instanced or the value simply assigned返回类型是实例化的还是简单地赋值
【发布时间】:2013-06-19 20:28:08
【问题描述】:

我一直在考虑我的问题的答案,所以我不得不问,返回类型是实例化的还是简单地赋值?

以下面的示例为例:

class Convert {

public:

    int getValue (std::string const& _text);

};


Convert convert;
int total(0);

total = convert.getValue("488");

因此;从getValue() 成员函数返回结果时发生了什么,步骤是什么?是创建了返回类型int 的实例,然后将值从临时int 复制到total 变量中,还是将返回值直接分配给total 而无需创建临时返回类型@ 987654327@?

因为我的一些代码有void getValue (int& _result, std::string const& _text);然而,int getValue (std::string const& _text) 更符合逻辑。

【问题讨论】:

  • 可以根据优化采取任何一种选择......正如你所说的按价值返回更合乎逻辑,你应该坚持......它更好的方法

标签: c++ return return-value


【解决方案1】:

复制省略是几乎所有编译器都支持的允许优化。

复制省略意味着在某些情况下,创建一个临时副本,然后将该临时副本复制到一个命名变量中,然后销毁该临时副本,可能会导致直接构造该变量。

与返回值优化和移动语义一起,意味着返回可移动的复杂对象是高效的。对于像int 这样的类型,as-if 规则也在运行中:编译器可以做任何表现得好像代码行或代码块被执行的事情,并且编译器了解当您复制/移动 @987654322 时会发生什么@ around(即,基本上什么都没有),所以他们可以跳过那些复制/移动。

为确保 RVO 和复制省略正确发生,您需要执行以下操作:

int function() {
  int retval = 8; // create the return value in one spot, this makes NRVO possible
  // code goes here
  return retval; // you can have more than one return statement in most compilers
}

int result = function(); // initialize the variable result by assigning the return value of the function to it.

如果您执行上述操作,大多数编译器将直接在result 变量存储中构造retval 对象,如果function 的主体可以在result 中看到,则根本不会发生任何副本(有些可能即使你看不到function mayhap 的身体,也要这样做)

在 C++11 中还有其他技巧。

int function() {
  return {7}; // guaranteed to directly construct the return value without a copy
}

int result = function(); // even if copy elision is not done, the temporary `int` is moved into `result`.

如果你阻止复制省略:

int function() {
  int foo = 7;
  int bar = 3;
  // code
  if (foo>bar)
    return foo;
  else
    return bar;
}

只要您返回一个局部变量,就会发生隐式移动。也可以显式地将std::move 放入返回值中。

现在,对于像int 这样简单而小型的类型,所有这些优化都毫无意义。当您处理更大、更昂贵的对象时,例如std::vectors 中的std::vector,每个对象中都有 10 MB 的数据,这些技术意味着按值返回最终与传递指向的指针一样有效小心填写。

【讨论】:

  • 谢谢雅克。我说你非常了解编译器!阅读你的解释使我理解的事情在很多方面都颠倒了。我不会猜到函数中具有相同类型的变量可以得到优化,从而将计算直接放入 result 变量中,而不需要临时变量。 ^^ 我只有一个后续问题,正如您所说“为了确保 RVO 和复制省略正确发生”,您有 int result = function(); 在对 'function()' 进行二次调用时刹车,就像result = function(); 一样,没有初始化?
  • @8-bitButterfly 第二个示例将复制或移动构造。对于像 int 这样的普通类型,编译器会将副本作为构造执行相同的操作,并且它可以跳过任何较早的构造,因为它知道您从未阅读过它。
【解决方案2】:

您的完整表达式total = convert.getValue("488"); 是一个赋值表达式。评估它会将右边的值分配给变量total。右边的值是评估子表达式convert.getValue("488"); 的结果。该值是int 类型的临时值(右值)。

我真的不认为有更好的说法是“将右侧评估结果的值分配给左侧”。

优化编译器很可能会意识到该类是空的,并且 total0 的初始化是不可观察的,并将所有内容都折叠成非常短的内容。

【讨论】:

    猜你喜欢
    • 2014-11-28
    • 1970-01-01
    • 2017-04-22
    • 1970-01-01
    • 1970-01-01
    • 2017-03-01
    • 1970-01-01
    相关资源
    最近更新 更多