C++ std move中无副本的赋值

C++ std move中无副本的赋值,c++,c++11,move,rvo,nrvo,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); 我想这应该是因为我避免了一个额外的副本 我想这应该是因为我避免了

我有一个返回映射的util函数

std::map getFooMap() {
  std::map foo;
  // ... populate the map
  return foo;
}
在调用方,我想将映射分配给某个对象的数据字段。我可以做到:

dest.data = getFooMap()
这会比下面的更快吗

auto temp = getFooMap();
dest.data = std::move(temp);
我想这应该是因为我避免了一个额外的副本

我想这应该是因为我避免了一个额外的副本

只要“
std::map
”是可移动的,您就可能只会避免一次额外的移动,优化器也可能会避免

性能差异可能可以忽略或不存在,但
dest.data=getFooMap()
更简单,而且可能不会更慢

正如在一篇评论中指出的,直接初始化
dest.data
而不是在构建之后分配它会更快。这可以通过在成员初始化器中调用
getFooMap
来实现

我想这应该是因为我避免了一个额外的副本

只要“
std::map
”是可移动的,您就可能只会避免一次额外的移动,优化器也可能会避免

性能差异可能可以忽略或不存在,但
dest.data=getFooMap()
更简单,而且可能不会更慢


正如在一篇评论中指出的,直接初始化
dest.data
而不是在构建之后分配它会更快。这可以通过在成员初始化器中调用
getFooMap
来实现。

从美学角度来看,它必须是第一个变体。您必须对语言标准和编译器作者有一定的信心,才能不需要像临时文件这样愚蠢的工件来促进优化

让我们检查一下情况是否确实如此。首先,请允许我稍微抽象一下您的示例,并将
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);
}
#包括
//最初这是一个std::map,替换为
//一个“不透明”类,用于缩短输出和
//防止内联和合并
//std映射相关代码与其余代码
结构foo{
foo()不例外;
foo(const foo&)无例外;
foo(foo&&)无例外;
foo&运算符=(foo&&)无例外;
foo&运算符=(const foo&)noexcept;
~foo()不例外;
};
struct dest{foo data;};
foo get_foo()无例外;
void do_stuff_(const dest&dest)无例外;
void move_from_mediate()noexcept{
目的地;
auto temp=get_foo();
目的地数据=标准::移动(温度);
与(目的地)一起工作;
}
void stright_赋值()无例外{
目的地;
dest.data=get_foo();
与(目的地)一起工作;
}
现在,如果我们看到GCC(trunk)为这两个函数生成相同的汇编代码。这包括:

  • 1建筑
  • 2破坏
  • 1移动作业
  • 1调用
    get\u foo()
  • 1调用
    do\u stuff\u with()

在这两种情况下。clang(trunk)也将生成相同的代码,甚至可以进行轻微的重新排序-但与GCC的代码并不完全相同。

从美学角度来看,它必须是第一个变体。您必须对语言标准和编译器作者有一定的信心,才能不需要像临时文件这样愚蠢的工件来促进优化

让我们检查一下情况是否确实如此。首先,请允许我稍微抽象一下您的示例,并将
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);
}
#包括
//最初这是一个std::map,替换为
//一个“不透明”类,用于缩短输出和
//防止内联和合并
//std映射相关代码与其余代码
结构foo{
foo()不例外;
foo(const foo&)无例外;
foo(foo&&)无例外;
foo&运算符=(foo&&)无例外;
foo&运算符=(const foo&)noexcept;
~foo()不例外;
};
struct dest{foo data;};
foo get_foo()无例外;
void do_stuff_(const dest&dest)无例外;
void move_from_mediate()noexcept{
目的地;
auto temp=get_foo();
目的地数据=标准::移动(温度);
与(目的地)一起工作;
}
void stright_赋值()无例外{
目的地;
dest.data=get_foo();
与(目的地)一起工作;
}
现在,如果我们看到GCC(trunk)为这两个函数生成相同的汇编代码。这包括:

  • 1建筑
  • 2破坏
  • 1移动作业
  • 1调用
    get\u foo()
  • 1调用
    do\u stuff\u with()

在这两种情况下。clang(trunk)也将生成相同的代码,甚至可以进行轻微的重新排序,但与GCC的代码并不完全相同。

好的编译器将优化两个调用,使其执行几乎相同的操作,但如果不是这样,则首选第一个。为什么要立即将返回的变量命名为
std::move
呢?是否检查了程序集?FWIW,我听说
std::move
禁止,所以对于一个好的编译器,我希望move-less选项比
std::move
更快,或者至少与
std::move
的选项一样快。最好是使用
getFooMap()
调用来构造
dest.data
,这通常意味着在构造
dest
时进行调用。这对您来说可能吗?好的编译器会优化两个调用,使其几乎做相同的事情,尽管如果不是这样,则首选第一个。为什么要立即将返回的变量命名为
std::move
呢?是否检查了程序集?FWIW,我听说
std::move
禁止,所以对于一个好的编译器,我希望move-less选项更快,或者至少与
std::move
的选项一样快。最好是使用
getFooMap()
调用来构造
dest.data
,这通常意味着在