在标准移动中没有复制的分配

时间:2019-01-08 23:41:43

标签: c++ c++11 move rvo nrvo

我有一个util函数,可以返回地图

std::map getFooMap() {
  std::map foo;
  // ... populate the map
  return foo;
}

从调用者那边,我想将地图分配给某个对象的数据字段。我可以的:

dest.data = getFooMap()

这会比以下速度更快吗?

auto temp = getFooMap();
dest.data = std::move(temp);

我认为应该避免复制一份吗?

2 个答案:

答案 0 :(得分:2)

  

我认为应该避免复制一份吗?

只要“ std::map”是可移动的,您就只能避免一个额外的移动-优化程序也可以避免该移动。

性能差异可能微不足道或根本不存在,但是dest.data = getFooMap()更简单并且可能不会更慢。

正如评论中指出的那样,直接初始化dest.data而不是在构造之后进行分配会更快。这可以通过在成员初始化程序中调用getFooMap来实现。

答案 1 :(得分:1)

从美学的角度来看,它是第一个变种。您必须对语言标准和编译器作者有一定的信心,以免像临时工具那样愚蠢的工件来促进优化。

让我们检查是否确实如此。首先,让我稍微抽象一下您的示例,并将map替换为类foo,而无需定义其各种方法;并假设没有抛出任何异常:

#include <utility>

// Originally this was an std::map, replaced with
// an "opaque" class to shorten the output and
//  prevent inlining and conflation of 
// std-map-related code with the rest of the code
struct foo {
    foo() noexcept;
    foo(const foo&) noexcept;
    foo(foo&&) noexcept;
    foo& operator=(foo&&) noexcept;
    foo& operator=(const foo&) noexcept;
    ~foo() noexcept;
};

struct dest_t { foo data; };

foo get_foo() noexcept;
void do_stuff_with(const dest_t& dest) noexcept;

void move_from_intermediate() noexcept {
    dest_t dest;
    auto temp = get_foo();
    dest.data = std::move(temp);
    do_stuff_with(dest);
}

void straight_assignment() noexcept {
    dest_t dest;
    dest.data = get_foo();
    do_stuff_with(dest);
}

现在,如果我们compile this on GodBolt,我们将看到GCC(主干)为两个函数产生相同的汇编代码。其中包括:

  • 1个建筑
  • 2次破坏
  • 1个动作分配
  • 打给get_foo()的电话
  • 打给do_stuff_with()的电话
在两种情况下

。 clang(trunk)也将产生相同的代码,直到稍微重新排序-但与GCC并不完全相同。