0

我有一个返回地图的 util 函数

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

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

dest.data = getFooMap()

这会比以下更快吗?

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

我认为这应该是因为我避免了一份额外的副本?

4

2 回答 2

2

我认为这应该是因为我避免了一份额外的副本?

只要 " std::map" 是可移动的,您就只能避免一次额外的移动——优化器也可能会避免这种移动。

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

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

于 2019-01-08T23:56:03.190 回答
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);
}

现在,如果我们在 GodBolt 上编译它,我们会看到 GCC(主干)为这两个函数生成相同的汇编代码。那包含着:

  • 1 建设
  • 2 次破坏
  • 1 移动任务
  • 1 呼叫get_foo()
  • 1 呼叫do_stuff_with()

在这两种情况下。clang (trunk) 也将产生相同的代码,直到轻微的重新排序——但与 GCC 的代码不完全相同。

于 2019-01-21T22:28:22.750 回答