10

假设我有:

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::movestd::forward<T>?为什么?

4

3 回答 3

6

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

std::forward<T>(value)

适合。

std::forward<T>(value)T &&当不知道是绑定到右值还是左值时,它是正确的选择。这里就是这种情况,因为在构造函数中我们不知道对于某些普通类型T &&是等价于,还是等价于.U &&UU & &&

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

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

于 2013-06-28T01:52:02.587 回答
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) ) { }
};

除非你正在做一些非常奇怪的事情。

非常奇怪,这意味着如果你T是一个int,你只想接受从值转移到你的Node,但如果你T是一个int&,你只接受非常量左值int,如果T是一个int const&,你接受任何可转换为的值int.

这将是对NodeBase. 我可以想到可能会出现这种情况的情况,但它们并不常见。

假设您只是想NodeBase存储该数据,T&&那么接受构造函数不是正确的做法——如果您存储的是intin NodeBase,您可能愿意复制 that int,而不是只接受 move-from int

上面的代码正是这样做的——它允许任何可以存储在 中的东西都可以NodeBase通过NodeBase完美的转发传递给 。

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

于 2013-06-28T02:25:24.953 回答
1

T在你的例子中没有推断出来。T是类模板参数,而不是函数模板参数。因此,假设您不会将引用类型用作 T,T&&它将是对 T 的右值引用,因此它只会绑定到 T 类型的右值。这些将是安全的,因此您可以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&&实际上可能是左值引用或右值引用。这只是因为T是在这种情况下推导出来的。

于 2013-06-28T00:52:16.107 回答