std :: move在类模板中的构造函数初始化列表中

时间:2013-06-10 22:42:37

标签: c++ c++11 move-semantics

我有一个这样的模板:

template<typename T>
struct foo {
  T m_t;
  foo(T t) : m_t(t) {}
};

问题是我想支持T的小/常规类型和巨大类型(如矩阵)。您是否建议我像这样编写构造函数初始化列表

foo (T t) : m_t(std::move(t)) {}

并要求类型T始终支持移动构造,即使是较小的类型?还有更好的方法吗?

3 个答案:

答案 0 :(得分:11)

  

并要求类型T始终支持移动构造,即使是较小的类型?

任何可复制构造的类型也可移动构造。在这些情况下移动只需调用复制构造函数。因此, 没有理由使用m_t(std::move(t))

另一种方法是使用引用:

foo (T const& t) : m_t(t) {}
foo (T&& t) : m_t(std::move(t)) {}

这样做的好处是只涉及一种结构而不是两种结构。

答案 1 :(得分:7)

是的,在这种情况下使用此举并没有不利之处。所有可复制对象都可以自动移动,因此无关紧要。实际上,有些人建议在可能的情况下始终移动变量,甚至是整数。

作为替代方案,您可以考虑使用完美转发,如this answer

中所述
template <typename T2>
foo(T2&& t) : m_t(std::forward<T2>(t)) {}

如果您知道T定义了快速移动构造函数,那么它应该无关紧要。否则,建议提供构造函数foo(const T&)以避免不必要的副本。

完美转发只是实现这一目标的一种技术。 Pubby解决构造函数foo(const T&)foo(T&&)的解决方案当然也没问题。结果是一样的,主要是风格问题。

您还询问了小整数类型。理论上,通过引用传递它们比复制它们要慢,但无论如何编译器应该能够将它优化为副本。我不认为这会有所作为。

因此,更好地优化T可能很大并且不提供快速移动构造函数的最坏情况。通过引用传递最适合这种情况,并且通常也不应该是一个糟糕的选择。

答案 2 :(得分:0)

按值传递的优点是只有一个构造函数(没有模板),但与前面提到的替代方案相比,其代价是一个额外的移动构造。

然而,Pubby给出了pass-by-value和非模板解决方案的另一个未提及的缺点:如果将复制构造函数定义为T(T&);,则移动不会起作用(注意引用非常数)。 Rvalue-references可以绑定到lvalue-references-to-const,但不能绑定到lvalue-references-to-non-const,因此编译器无法调用复制构造函数。

要解决此问题,您只需向Pubby的解决方案添加第三个重载foo (T & t) : m_t(t) {}即可。