我在这里使用std :: forward或std :: move吗?

时间:2013-06-28 00:45:41

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

让我说我有:

template<class T>
struct NodeBase
{
    T value;
    NodeBase(T &&value)
        : value(value) { }
};

我继承了它:

template<class T>
struct Node : public NodeBase<T>
{
    Node(T &&value)
        : NodeBase( WHAT_GOES_HERE (value)) { }
};

WHAT_GOES_HEREstd::move还是std::forward<T>?为什么呢?

3 个答案:

答案 0 :(得分:6)

因为在Node<T>的构造函数的实现中,不知道T是普通类型(即不是引用)还是引用,

std::forward<T>(value)

是合适的。

每当不知道std::forward<T>(value)是否绑定到右值或左值时,

T &&是正确的选择。这是这种情况,因为在构造函数中,我们不知道T &&对于某些普通类型U &&是等同于U,还是等同于U & &&

在使用T或在不同时间确定的函数调用中是否推导出std::forward并不重要(例如在您的示例中,T在实例化Node模板的时间。)

std::forward<T>(value)将以与调用者直接调用基类构造函数相同的方式调用继承的构造函数。也就是说,当value是左值时它会在左值上调用它,而当value是右值时它会在左值上调用它。

答案 1 :(得分:4)

可能都没有。

我怀疑你应该拥有的是:

template<class T>
struct NodeBase
{
  T value;
  NodeBase(NodeBase &&) = default;
  NodeBase(NodeBase const&) = default; // issue: this might not work with a `T&`, but we can conditionally exclude it through some more fancy footwork
  NodeBase(NodeBase &) = default;
  template<typename U, typename=typename std:enable_if< std::is_convertible<U&&, T>::value >::type >
  NodeBase(U &&u)
    : value(std::forward<U>(u)) { }
};

template<class T>
struct Node : public NodeBase<T>
{
  Node( Node & ) = default;
  Node( Node const& ) = default; // issue: this might not work with a `T&`, but we can conditionally exclude it through some more fancy footwork
  Node( Node && ) = default;
  template<typename U, typename=typename std:enable_if< std::is_convertible<U&&, NodeBase<T>>::value >::type>
  Node(U && u)
    : NodeBase( std::forward<U>(u) ) { }
};

除非你做的事非常奇怪。

非常奇怪,这意味着如果您的Tint,您只想接受移动的值到Node,但是如果您的Tint&,您只接受非常量左值int,如果Tint const&,则您接受任何可转换为int的值。< / p>

这将是一组奇怪的要求放在NodeBase的构造函数上。我可以想到可能出现这种情况的情况,但它们并不常见。

假设您只是希望NodeBase存储该数据,那么在构造函数中使用T&&不是正确的做法 - 如果您在int中存储NodeBase },您可能愿意复制int,而不是只接受来自int的移动。

上面的代码就是这样 - 它允许将NodeBase中存储的任何内容传递到所述NodeBase,并完美转发。

另一方面,如果你真的想要一套奇怪的施工限制,那么这对你来说不是正确的答案。当我构建一个从通用引用参数构建的template类型时,我已经使用了它,我确实想要限制传入的类型以完全匹配通用引用参数,并在参数中存储它是一个右值参考,否则保留对它的引用。

答案 2 :(得分:1)

您的示例中未推断出

TT是类模板参数,而不是函数模板参数。因此,假设您不会将引用类型用作T,T&&将是对T的右值引用,因此它只会绑定到类型为T的rvalues。这些可以安全移动,因此您可以使用{{1这里。

std::move

template<class T> struct Node : public NodeBase<T> { Node(T &&value) : NodeBase( std::move (value)) { } }; int main() { int i = 3; Node<int> x(42); // ok Node<int> y(std::move(i)); // ok Node<int> z(i); // error } 通常仅适用于您的推导类型可能是左值引用或右值引用的地方。

std::forward

因为template<class T> void f(T&& x) { ... std::forward<T>(x) ... } 实际上可能是左值引用或右值引用。这只是因为在此上下文中推断出T&&