简体   繁体   English

完美的转发和模板

[英]Perfect forwarding and templates

When I have a code which looks like this: 当我有一个看起来像这样的代码时:

template<class T>
void f_(const T& arg)
{
    cout << "void f(const T& arg): Cannot modify\n";
}

template<class T>
void f_(T&& arg)
{
    cout << "void f(T&& arg): Can modify\n";
}

and in main I call it: 总的来说,我称它为:

int main()
{

    MemoryBlock block;
    f_(block);
    f_(MemoryBlock());
    return 0;
}

The output is: 输出为:
"void f(T&& arg): Can modify\\n"; “ void f(T && arg):可以修改\\ n”;
"void f(T&& arg): Can modify\\n"; “ void f(T && arg):可以修改\\ n”;

But when I change this code to non-generic, that is instead of function templates I'll have regular functions, 但是,当我将此代码更改为非通用代码时,我将使用常规函数代替函数模板,

void f(const MemoryBlock&)
{
    cout << "In f(const MemoryBlock&). This version cannot modify the parameter.\n";
}

void f(MemoryBlock&&)
{
    cout << "In f(MemoryBlock&&). This version can modify the parameter.\n";
}

the output is more "intuitive": 输出更加“直观”:
"In f(const MemoryBlock&). This version cannot modify the parameter."; “在f(const MemoryBlock&)中。此版本无法修改参数。”;
"In f(MemoryBlock&&). This version can modify the parameter."; “在f(MemoryBlock &&)中。此版本可以修改参数。”;

It looks to me that only by changing functions from being templates to non templates changes totally the deduction rules for rvalue references. 在我看来,只有通过将功能从模板更改为非模板,才能完全改变右值引用的推导规则。
Will be really greateful if somebody would explain that to me. 如果有人向我解释这一点,那将是非常伟大的。

When you use T&& , that's not an rvalue reference, that's a universal reference parameter. 当使用T&& ,这不是右值引用,而是通用引用参数。 They're declared the same way, but they behave differently. 它们的声明方式相同,但行为不同。

When you remove the template parameters, you're no longer in a deducible context and it's actually an rvalue reference: they bind only to rvalues, of course. 删除模板参数后,您将不再处于可推论的上下文中,而实际上是一个右值引用:当​​然,它们仅绑定到右值。

In a deducible context (that is when type deduction is taking place), T&& can be either an rvalue reference or an lvalue reference. 在可推论的上下文中(即发生类型推论时), T&&可以是右值引用左值引用。 Universal references can bind to virtually all combinations ( const , const volatile , etc.) and in your case: const T& . 通用引用实际上可以绑定到所有组合( constconst volatile等),在您的情况下为const T&

Now your train of thought was to be efficient and overload for rvalues and then lvalues, but what happens is that the universal reference overload is a better match when deducing template arguments. 现在,您的思路是高效且重载rvalues和lvalues,但是发生的是,推导模板参数时,通用引用重载是更好的匹配。 Therefore, it will be selected over the const T& overload. 因此,将选择const T&重载。

Usually, you only want to keep the universal reference function and pair it up with std::forward<T>() to perfect forward the argument(s). 通常,您只想保留通用引用函数并将其与std::forward<T>()配对以完善转发参数。 This removes the need for your const T& overload, since the universal reference version will take over. 由于通用参考版本将接管,因此不需要const T&重载。

Please note that just because you see && in a deducible context, it does not mean that it is an universal reference; 请注意,仅仅因为您在可推论的上下文中看到&& ,并不意味着它是通用参考。 the && needs to be appended to the type being deduced, so here's an example of something that is actually an rvalue reference: &&需要附加到要推导的类型上,因此这是一个实际上是右值引用的示例:

template<class T>
void f_(std::vector<T>&& arg) // this is an rvalue reference; not universal
{
    cout << "void f(T&& arg): Can modify\n";
}

Here's a great talk on the matter: https://channel9.msdn.com/Shows/Going+Deep/Cpp-and-Beyond-2012-Scott-Meyers-Universal-References-in-Cpp11 这是关于此事的精彩演讲: https : //channel9.msdn.com/Shows/Going+Deep/Cpp-and-Beyond-2012-Scott-Meyers-Universal-References-in-Cpp11

T&& can be called as universal/forwarding reference. T&&可称为universal/forwarding参考。
reference collapsing rules: 参考折叠规则:

  1. A& & becomes A& A&&成为A&
  2. A& && becomes A& A&&&成为A&
  3. A&& & becomes A& A &&&成为A&
  4. A&& && becomes A&& A && &&成为A &&

template<typename T> void foo(T&&);

Here, the following apply: 在此适用以下条件:

  1. When foo is called on an lvalue of type A, then T resolves to A& and hence, by the reference collapsing rules above, the argument type effectively becomes A&. 当在类型A的左值上调用foo时,T解析为A&,因此,根据上面的引用折叠规则,自变量类型实际上变为A&。
  2. When foo is called on an rvalue of type A, then T resolves to A, and hence the argument type becomes A&&. 当对类型A的右值调用foo时,T解析为A,因此参数类型变为A &&。

In your case: 在您的情况下:

template<class T> void f_(T&& arg);
f_(block); //case 1
f_(MemoryBlock()); //case  2

In case 1: 情况一:
T = MemoryBlock& then T&& becomes T& && ==> gives T& T = MemoryBlock&,然后T &&变为T&&& ==>给出T&
In case 2: 情况2:
T = MemoryBlock then T&& becomes T&& ==> gives T&& T = MemoryBlock,然后T &&变为T && ==>给出T &&

For these both case 对于这两种情况

template<class T> void f_(T&& arg)

is the best choice for the compiler hence its taken instead of 是编译器的最佳选择,因此采用它代替

template<class T>
void f_(const T& arg)

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

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