我有一个这样的模板:
template<typename T>
struct foo {
T m_t;
foo(T t) : m_t(t) {}
};
问题是我想支持T
的小/常规类型和巨大类型(如矩阵)。您是否建议我像这样编写构造函数初始化列表
foo (T t) : m_t(std::move(t)) {}
并要求类型T
始终支持移动构造,即使是较小的类型?还有更好的方法吗?
答案 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) {}
即可。