通过输入参数多次传递完美转发 [英] Perfect forwarding with multiple passes over input arguments

查看:149
本文介绍了通过输入参数多次传递完美转发的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

请考虑以下函数 accept ,它使用类型 T 的通用引用,并将其转发到 parse< T>()函数对象,对于左值重载和右值重载:

Consider the following function accept that takes a "universal reference" of type T and forwards that to a parse<T>() function object with an overload for lvalues and one for rvalues:

template<class T>
void accept(T&& arg)
{
    parse<T>()(std::forward<T>(arg), 0); // copy or move, depending on rvaluedness of arg
}

template<class T>
class parse
{
    // parse will modify a local copy or move of its input parameter
    void operator()(T const& arg, int n) const { /* optimized for lvalues */ }
    void operator()(T&& arg)    , int n) const { /* optimized for rvalues */ }
};

由于完美的转发使源对象处于有效但未定义的状态,在同一范围内。下面我试图在一个假设的 split()函数中尽可能少的副本,它接受一个 int

Since perfect forwarding leaves the source object in a valid but undefined state, it is impossible to perfectly forward again within the same scope. Below my attempt to have as few copies as possible in a hypothetical split() function that takes an int representing the number passes that have to be made over the input data:

template<class T>
void split(T&& arg, int n)
{
    for (auto i = 0; i < n - 1; ++i)
        parse<T>()(arg , i);                 // copy n-1 times
    parse<T>()(std::forward<T>(arg), n - 1); // possibly move the n-th time
}

问题:这是推荐的方式来应用完美转发多次通过相同的数据?如果没有,最小化副本数量的更惯用的方法是什么?

Question: is this the recommended way to apply perfect forwarding for multiple passes over the same data? If not, what is a more idiomatic way to minimize the number of copies?

推荐答案


问题:是否是针对相同数据对多次传递完美转发的推荐方式?

Question: is this the recommended way to apply perfect forwarding for multiple passes over the same data?

是当您需要多次传递数据时应用完美转发(或移动)的建议方式。只有(可能)从您的最后访问。事实上,在原始移动纸张,并且是非常的原因,命名变量声明与类型rvalue-reference不隐式移动。从 N1377

Yes, this is the recommended way to apply perfect forwarding (or move) when you need to pass the data multiple times. Only (potentially) move from it on your last access. Indeed, this scenario was foreseen in the original move paper, and is the very reason that "named" variables declared with type rvalue-reference are not implicitly moved from. From N1377:


即使命名的右值引用可以绑定到右值,它们也是
当使用时被视为左值。例如:

Even though named rvalue references can bind to an rvalue, they are treated as lvalues when used. For example:



struct A {};

void h(const A&);
void h(A&&);

void g(const A&);
void g(A&&);

void f(A&& a)
{
    g(a);  // calls g(const A&)
    h(a);  // calls h(const A&)
}




虽然右值可以绑定到f()的a参数,一旦绑定,
现在被视为左值。特别地,对重载的
函数g()和h()的调用解析为const A& (lvalue)重载。
将a视为f中的右值将导致容易出错的代码:
首先调用g()的移动版本,这可能是
pilfera然后被窃取的a将被发送到移动
重载的h()。

Although an rvalue can bind to the "a" parameter of f(), once bound, a is now treated as an lvalue. In particular, calls to the overloaded functions g() and h() resolve to the const A& (lvalue) overloads. Treating "a" as an rvalue within f would lead to error prone code: First the "move version" of g() would be called, which would likely pilfer "a", and then the pilfered "a" would be sent to the move overload of h().

如果你想要 h(a)在上面的例子中移动,你必须明确这样做:

If you want h(a) to move in the above example, you have to do so explicitly:

    h(std::move(a));  // calls h(A&&);

由于 Casey 在评论中指出,传递lvalue时会出现重载问题:

As Casey points out in the comments, you have an overloading problem when passing in lvalues:

#include  <utility>
#include  <type_traits>

template<class T>
class parse
{
    static_assert(!std::is_lvalue_reference<T>::value,
                               "parse: T can not be an lvalue-reference type");
public:
    // parse will modify a local copy or move of its input parameter
    void operator()(T const& arg, int n) const { /* optimized for lvalues */ }
    void operator()(T&& arg     , int n) const { /* optimized for rvalues */ }
};

template<class T>
void split(T&& arg, int n)
{
    typedef typename std::decay<T>::type Td;
    for (auto i = 0; i < n - 1; ++i)
        parse<Td>()(arg , i);                 // copy n-1 times
    parse<Td>()(std::forward<T>(arg), n - 1); // possibly move the n-th time
}

Casey建议,通过使用 std :: decay 只在非引用类型上实例化 parse< T> 。我还添加了一个static_assert来确保客户端不会意外犯这个错误。 static_assert 不是绝对必要的,因为你将得到一个编译时错误。但是 static_assert 可以提供更加可读的错误消息。

Above I've fixed it as Casey suggests, by instantiating parse<T> only on non-reference types using std::decay. I've also added a static_assert to ensure that the client does not accidentally make this mistake. The static_assert isn't strictly necessary because you will get a compile-time error regardless. However the static_assert can offer a more readable error message.

这不是解决问题的唯一方法。另一种方式允许客户端使用引用类型实例化 parse 是部分专门解析:

That is not the only way to fix the problem though. Another way, which would allow the client to instantiate parse with an lvalue reference type, is to partially specialize parse:

template<class T>
class parse<T&>
{
public:
    // parse will modify a local copy or move of its input parameter
    void operator()(T const& arg, int n) const { /* optimized for lvalues */ }
};

现在客户端不需要执行衰减 dance:

Now the client doesn't need to do the decay dance:

template<class T>
void split(T&& arg, int n)
{
    for (auto i = 0; i < n - 1; ++i)
        parse<T>()(arg , i);                 // copy n-1 times
    parse<T>()(std::forward<T>(arg), n - 1); // possibly move the n-th time
}

parse< T&>

这篇关于通过输入参数多次传递完美转发的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆