正确使用通用引用 [英] Proper use of universal references

查看:172
本文介绍了正确使用通用引用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在c ++ 11之前,我曾经写过这样的代码:

Before c++11, I used to write code like this:

// Small functions
void doThingsWithA(const A& a)
{
    // do stuff
}

void doThingsWithB(const B& b)
{
    // do stuff
}

void doThingsWithC(const C& c)
{
    // do stuff
}

// Big function
void doThingsWithABC(const A& a, const B& b, const C& c)
{
    // do stuff
    doThingsWithA(a);
    doThingsWithB(b);
    doThingsWithC(c);
    // do stuff
}

但现在使用move语义可能变得有趣(至少在某些情况下),允许我的函数将参数引用作为参数并添加这些重载:

But now, with move semantics, it may become interesting (at least in some cases) to allow my functions to take rvalue references as parameters and add these overloads:

void doThingsWithA(A&& a);
void doThingsWithB(B&& b);
void doThingsWithC(C&& c);



从我收集的,如果我想能够调用这些重载在我的大功能,需要使用完美的转发,这可能看起来像这样(它是一个较少的可读性,但我想它可以确定与一个良好的命名约定模板类型):

From what I gather, if I want to be able to call those overloads within my big function, I need to use perfect forwarding, which may look like this (it is a bit less readable, but I guess that it can be ok with a good naming convention for the template types):

template<typename TplA, typename TplB, typename TplC>
void doThingsWithABC(TplA&& a, TplB&& b, TplC&& c)
{
    // do stuff
    doThingsWithA(std::forward<TplA>(a));
    doThingsWithB(std::forward<TplB>(b));
    doThingsWithC(std::forward<TplC>(c));
    // do stuff
}

我的问题是:这意味着如果我的小的函数有其他重载,它将变得可能调用的参数的类型的参数的不是它的意图?

My problem is this: doesn't that mean that if my small functions have other overloads, it will become possible to call the big one with parameters of types for which it was not intended?

我认为这个可以工作以防止这种情况:

I think that this may work to prevent this:

template<typename TplA, typename TplB, typename TplC,
class = typename std::enable_if<std::is_same<A, std::decay<TplA>::type>::value>::type,
class = typename std::enable_if<std::is_same<B, std::decay<TplB>::type>::value>::type,
class = typename std::enable_if<std::is_same<C, std::decay<TplC>::type>::value>::type>
    doThingsWithABC(TplA&& a, TplB&& b, TplC&& c)
{
    // do stuff
    doThingsWithA(std::forward<TplA>(a));
    doThingsWithB(std::forward<TplB>(b));
    doThingsWithC(std::forward<TplC>(c));
    // do stuff
}

虽然我不知道不太严格,因为我不知道如果我试图调用的大型函数类型隐式转换为A,B或C它的行为...

Though I am not sure if it is not too restrictive, as I have no idea of how it behaves if I try to call the big functions with types that are implicitly convertible to A,B or C...

但是......即使假设这个工作,我真的没有其他选择吗? (我的意思是...这是不容易的眼睛)

But... even supposing this works, do I really have no other options? (I mean... it's not easy on the eyes)

推荐答案

完美转发主要是当你不知道如何数据将被消费,因为你正在写一个用户提供数据的通用包装。

Perfect forwarding is mainly for when you do not know how the data is going to be consumed, because you are writing a generic wrapper of 'user-supplied' data.

在一个简单的程序系统中,如上所述,你会做具体的任务。

In a simple procedural system like you describe above, the 3 things you do will be concrete tasks.

这意味着你会知道他们是否会受益于一个可移动的数据源,如果他们有意义

That means you will know if they would benefit from having a movable source of data or not, and if they make sense if they have to copy, and if move is cheap.

如果复制有意义,但移动速度较快,移动速度较快(常见情况),则应使用参数

If copy makes sense, but move is faster, and move is cheap (a common case), they should take parameters by value and move out of them when they store their local copy.

此规则会递归地应用于调用3个子函数的函数。

This rule then applies recursively to the function that calls the 3 sub functions.

如果函数不能从移动中获益,则可以通过 const&

If the function does not benefit from moving, take by const&.

如果复制没有意义,可以通过右值引用(不是通用引用)或值。

If copy does not make sense, take by rvalue reference (not a universal reference) or by value.

能够移动 移动仍然昂贵,如果你考虑完美的转发。如上所述,这通常只发生在由你的代码库的用户设置的包装函数时,因为通常 move 真的很便宜,或者像复制一样昂贵。您必须处于移动效率的中间或不确定阶段,以便完美转发才有价值。

In the case where it is both good to be able to move and move remains expensive should you consider perfect forwarding. As noted above, this usually only happens when wrapping functions set by the 'user' of your code base, as usually move is either really really cheap, or as expensive as copy. You have to be in an intermediate or indeterminate stage of move efficiency for perfect forwarding to be worthwhile.

其他用途完美转发,如容器mutator,但他们更深奥。作为一个例子,我的向后范围mutator将完美转发传入范围到存储,以便参考生存期扩展工作正常,当链接多个范围的mutators在C ++ 11风格基于范围的 for(:)循环。

There are other uses for perfect forwarding, such as container mutators, but they are more esoteric. As an example, my backwards range mutator will perfect forward the incoming range into storage in order to have reference lifetime extension work properly when you chain multiple range mutators in C++11 style ranged-based for(:) loops.

完全转发导致生成的代码膨胀,实现和难以理解的代码。

Madly perfect forwarding results in generated code bloat, slow builds, leaky implementations, and hard to understand code.

这篇关于正确使用通用引用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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