简体   繁体   English

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

[英]Do I use std::forward or std::move here?

Let's say I have: 比方说我有:

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

and I inherit from it: 我继承了它:

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

Should WHAT_GOES_HERE be std::move or std::forward<T> ? WHAT_GOES_HERE应该是std::move还是std::forward<T> Why? 为什么?

Since in the implementation of the constructor of Node<T> it is unknown whether T is a plain type (ie not a reference), or a reference, 因为在Node<T>的构造函数的实现中,不知道T是普通类型(即不是引用)还是引用,

std::forward<T>(value)

is suitable. 适合。

std::forward<T>(value) is the right choice whenever it isn't known whether T && binds to an rvalue or an lvalue. 只要不知道T &&是否绑定到右值或左值, std::forward<T>(value)就是正确的选择。 This is the case here because in the constructor we don't know whether T && is equivalent to U && for some plain type U , or equivalent to U & && . 这是这种情况,因为在构造函数中我们不知道T &&是否等同于U &&对于某些普通类型U ,或等效于U & &&

It doesn't matter whether T is deduced in the function call that uses std::forward or determined at a different time (such as in your example, where T is determined at the time when the Node template is instantiated). 在使用std::forward或在不同时间确定的函数调用中是否推导出T并不重要(例如在您的示例中,其中T是在实例化Node模板时确定的)。

std::forward<T>(value) will call the inherited constructor in the same way as if the base class constructor had been called directly by the caller. std::forward<T>(value)将以与调用者直接调用基类构造函数相同的方式调用继承的构造函数。 Ie, it will call it on an lvalue when value is an lvalue, and on an rvalue when value is an rvalue. 也就是说,当value是左值时,它将在左value上调用它,当value是右值时,它将在右value上调用它。

Probably neither. 可能都没有。

What I suspect you should have is: 我怀疑你应该拥有的是:

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) ) { }
};

unless you are doing something exceedingly strange. 除非你做的事情非常奇怪。

By exceedingly strange, it means that if your T is an int , you want to only accept moved-from values into your Node , but if your T is an int& , you accept only non-const lvalue int s, and if T is an int const& , you accept any value convertible to int . 非常奇怪,这意味着如果你的T是一个int ,你只想接受移动的值到你的Node ,但是如果你的T是一个int& ,你只接受非const lvalue int ,如果T是一个int const& ,您接受任何可转换为int值。

This would be a strange set of requirements to place on the constructor of NodeBase . 这将是一组奇怪的要求放在NodeBase的构造函数上。 I can think of situations where this might be the case, but they are not common. 我可以想到可能出现这种情况的情况,但它们并不常见。

Assuming you simply want NodeBase to store that data, taking T&& in the constructor is not the right thing to do -- if you are storing an int in NodeBase , you probably are willing to make copies of that int , instead of only accepting moved-from int s. 假设你只是想让NodeBase存储那些数据,那么在构造函数中使用T&&是不对的 - 如果你在NodeBase中存储一个int ,你可能愿意复制那个int ,而不是只接受移动 -来自int

The above code does exactly that -- it allows anything that could be stored in the NodeBase to be passed on up to said NodeBase , with perfect forwarding. 上面的代码就是这样 - 它允许任何可以存储在NodeBase中的NodeBase传递到所述NodeBase ,并具有完美的转发功能。

On the other hand, if you actually want the strange set of construction restrictions, then this is not the right answer for you. 另一方面,如果你真的想要一组奇怪的结构限制,那么这对你来说不是正确的答案。 I've used that when I was building the a template type that was built from a universal reference argument, and I did want to restrict the passed in type to match the universal reference argument exactly, and store it iff the argument was an rvalue reference, and otherwise keep a reference to it. 我在构建从通用引用参数构建的template类型时使用了它,并且我确实想要限制传入的类型以完全匹配通用引用参数,并且如果参数是rvalue引用则存储它,并以其他方式保留对它的引用。

T isn't deduced in your example. 在您的示例中没有推断出T The T is a class template parameter, not a function template parameter. T是类模板参数,而不是函数模板参数。 So assuming you will not use a reference type as T, T&& will be an rvalue-reference to T, so it will only bind to rvalues of type T. these will be safe to move so you can use std::move here. 所以假设你不会将引用类型用作T, T&&将是对T的rvalue引用,因此它只会绑定到类型为T的rvalues。这些移动是安全的,所以你可以使用std::move here。

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 is normally only for places where you have a deduced type that may either be an lvalue-reference or rvalue-reference. std::forward通常仅适用于您具有推导类型的地方,可以是左值引用或右值引用。

template<class T>
void f(T&& x)
{
    ... std::forward<T>(x) ...
}

Because T&& may actually be either an lvalue-reference or rvalue-reference. 因为T&&实际上可能是左值引用或右值引用。 This is only because T is deduced in this context. 这只是因为在这种情况下推断出T

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM