接受值或rvalue引用的仅移动参数

时间:2016-07-01 08:22:22

标签: c++ c++11

此帖Pass by value vs pass by rvalue reference的接受回答是:

  

对于仅移动类型(如std::unique_ptr),传值似乎是常态......

我对此有点怀疑。让我们说有一些不可复制的类型Foo,它也不便宜移动;以及某个类型Bar,其成员为Foo

class Foo {
public:
    Foo(const Foo&) = delete;
    Foo(Foo&&) { /* quite some work */ }
    ...
};

class Bar {
public:
    Bar(Foo f) : f_(std::move(f)) {}    // (1)
    Bar(Foo&& f) : f_(std::move(f)) {}  // (2)
    // Assuming only one of (1) and (2) exists at a time

private:
    Foo f_;
};

然后是以下代码:

Foo f;
...
Bar bar(std::move(f));

构造函数(1)引发2个移动构造,而构造函数(2)只引发1.我还记得在Scott Meyers的 Effective Modern C ++ 中读到这个但是不能记住哪一项立即。

所以我的问题是,对于仅限移动类型(或者更一般地说,当我们想要转移参数的所有权时),我们是否应该优先选择rvalue-reference来获得更好的性能?

更新:我知道按值传递构造函数/赋值运算符(有时称为统一 ctors /赋值运算符)可以帮助消除重复代码。我应该说我对(1)性能很重要,以及(2)类型是不可复制的情况更感兴趣,因此没有重复的ctors /赋值运算符接受const左值参考参数。

更新2:所以我找到了Scott Meyers关于具体问题的博客:http://scottmeyers.blogspot.com/2014/07/should-move-only-types-ever-be-passed.html。这篇博客讨论了他在 Effective Modern C ++ 的第41项中提倡的原因:

  

考虑仅为可复制参数 ...传递值便宜移动 ... [和] 始终复制。< / p>

在该项目中有关于按值传递与右值参考的广泛讨论,在此处引用太多。关键是,两种方式都有各自的优缺点,但是为了转移仅移动对象的所有权,通过右值引用似乎更为可取。

2 个答案:

答案 0 :(得分:1)

在这种情况下,我们可以吃蛋糕然后吃。仅为Foo类引用启用的模板构造函数为我们提供了完美的转发以及构造函数的单个实现:

#include <iostream>
#include <utility>

class Foo {
public:
    Foo() {}
    Foo(const Foo&) = delete;
    Foo(Foo&&) { /* quite some work */ }
};

class Bar {
public:
  template<class T, std::enable_if_t<std::is_same<std::decay_t<T>, Foo>::value>* = nullptr>
    Bar(T&& f) : f_(std::forward<T>(f)) {}  // (2)
    // Assuming only one of (1) and (2) exists at a time

private:
    Foo f_;
};

int main()
{
  Foo f;
  Bar bar(std::move(f));

  // this won't compile
//  Foo f2;
//  Bar bar2(f2);

}

答案 1 :(得分:0)

背景

很难想象一个移动成本昂贵的类:当语义允许时,移动语义完全取决于为副本提供 fast 替代方案的需要。

您带来了std::string和SSO的示例。然而,这个例子显然存在缺陷(我怀疑它们甚至开启了优化),因为通过memcpy复制16个字节应该占用一堆CPU周期,因为它可以在1个SIMD指令中实现,以便一次性存储它们。此外,MSVC 10真的很老了。

  

所以我的问题是,对于仅移动类型(或更一般地说,当我们   想转移论证的所有权),我们不应该更喜欢   传递右值参考以获得更好的性能?

我不会谈论表现,因为它是如此奇特的方面,不能“一般”分析。我们需要具体案例。此外,还需要考虑编译器优化;实际上相当重。不要忘记彻底的性能分析。

std::unique_ptr有点不同,因为(i)只能由于拥有语义而被移动(ii) 移动便宜。

我的观点。我想如果你必须提供一个“更快”的替代品作为API,请提供两者 - 就像std::vector::push_back一样。正如你所说,它可能会有轻微的改进 否则,即使对于仅移动类型,通过const-reference传递仍然有效,如果你认为它不会,则转换为pass-by-value。