【问题标题】:Pass-by-value or pass-by-universal-reference for sink arguments?接收器参数的按值传递或按通用引用传递?
【发布时间】:2014-07-11 05:35:59
【问题描述】:

写。建议的重复"Is pass-by-value a reasonable default in C++11?" - 那里的问题也没有那里的答案没有提到“通用参考”构造函数版本,所以I 真的看不到重复。考虑重新开放。


我正在熟悉移动语义,正在尝试它。请看一下这段(可编译的)代码:

#include <iostream>
#include <string>

struct my_str {
  std::string s;

  my_str(const std::string & str): s(str) { std::cout << "  my_str parameter ctor" << std::endl; }
  my_str(const my_str & o): s(o.s)        { std::cout << "  my_str copy ctor" << std::endl; }
  my_str(my_str && o): s(std::move(o.s))  { std::cout << "  my_str move ctor" << std::endl; }
};

template <typename T>
my_str build_ur(T && s) {
  return my_str(std::forward<T>(s));
}

my_str build_val(my_str s) {
  return my_str(std::move(s)); 
}

int main() {
  my_str s1("hello");
  my_str s2("world");

  std::cout << "Building from universal reference (copy):" << std::endl;
  build_ur(s1);
  std::cout << "Building from universal reference (move):" << std::endl;
  build_ur(std::move(s1));

  std::cout << "Building from value (copy):" << std::endl;
  build_val(s2);               
  std::cout << "Building from value (move):" << std::endl;
  build_val(std::move(s2));    

  std::cout << std::endl;
  return 0;
}

输出:

g++-4.8 -std=c++11 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
  my_str parameter ctor
  my_str parameter ctor
Building from universal reference (copy):
  my_str copy ctor
Building from universal reference (move):
  my_str move ctor
Building from value (copy):
  my_str copy ctor
  my_str move ctor
Building from value (move):
  my_str move ctor
  my_str move ctor

http://coliru.stacked-crooked.com/a/3be77626b7ca6f2c

这两个函数在这两种情况下都做得很好。按值函数再次调用移动构造函数,但这应该很便宜。您能否评论一下哪种模式应该优先于另一种模式的情况?

【问题讨论】:

  • 我会选择 my_str build(T && s),因为 my_str build(my_str s) 将移动局部函数变量 s(传递给函数时将被复制构造)跨度>
  • 你用的是什么编译器?第一个版本绝对应该使用复制ctor。 ideone.com/L8ZrI5
  • @Dario:嗯,你在使用一些优化吗?我实际上用我的 gcc 4.8.1 看到了一个 ctor
  • @Exceptyon 我更新了代码,现在我的意图应该更清楚了。
  • @Jarod42:你错了。移出对象位于"unspecified but valid state" 中。这不是 UB。

标签: c++ c++11 move forwarding-reference


【解决方案1】:

你的两个版本的build 做不同的事情。

版本 1:“创建副本”

my_str build_plain(my_str s) {
  return my_str(std::move(s)); 
}

使用复制构造函数创建参数的副本。然后将此副本移动到结果对象中。赋予该函数的对象将在之后有效。这个函数没有多大意义,因为my_str build(my_str s) { return s; } 会实现同样的事情。

版本 2:“移动它”

template <typename T>
my_str build_templated(T && s) {
  return my_str(std::forward<T>(s));
}

将参数对象移动到一个新对象中并返回该新对象。之后赋予函数的对象将无效!这是移动物体的经典案例。


总结一下:版本 1(几乎)等同于 my_str t = s;,而版本 2(几乎)等同于 my_str t = std::move(s);

【讨论】:

  • clang 和 gcc 在 main 中第二次调用时,即使使用第一个函数也不会调用任何复制 ctor。请更好地检查!
  • 看这里:ideone.com/L8ZrI5 甚至在您提供的链接中:coliru.stacked-crooked.com/a/5c2f3e3eefd4bc7c。两者都打印my_str copy ctor
  • 是的,在我想复制它的第一次通话中。在第二种情况下,如果需要移动,则在两种情况下都不会调用复制构造函数..
  • 我是否另有说明? “两种情况”是什么意思?您的代码中只有一种情况使用了第二个版本:build(std::move(s));
  • 我更新了代码,现在应该更清楚了。如果最初的版本造成混乱,请见谅。
猜你喜欢
  • 2012-01-16
  • 2012-10-14
  • 1970-01-01
  • 2019-05-07
  • 2012-03-12
  • 2013-09-15
  • 1970-01-01
  • 2014-08-23
  • 2015-05-23
相关资源
最近更新 更多