右值模板参数隐式用作左值,并且 std::转发工作

rvalue template argument implicitly used as lvalue, and std::forwarding working

本文关键字:并且 std 转发 工作 参数      更新时间:2023-10-16

这个关于std::forward用法的例子让我感到困惑。这是我编辑的版本:

#include <iostream>
#include <memory>
#include <utility>
using namespace std;
struct A{
    A(int&& n) { cout << "rvalue overload, n=" << n << "n"; }
    A(int& n)  { cout << "lvalue overload, n=" << n << "n"; }
};
template<typename> void template_type_dumper();
template<class T, class U>
unique_ptr<T> make_unique(U&& u){
    //Have a "fingerprint" of what function is being called
    static int dummyvar;
    cout<<"address of make_unique::dummyvar: "<<&dummyvar<<endl;
    //g++ dumps two warnings here, which reveal what exact type is passed as template parameter
    template_type_dumper<decltype(u)>;
    template_type_dumper<U>;
    return unique_ptr<T>(new T(forward<U>(u)));
}
int main()
{
    unique_ptr<A> p1 = make_unique<A>(2); // rvalue
    int i = 1;
    unique_ptr<A> p2 = make_unique<A>(i); // lvalue
}

输出为

address of make_unique::dummyvar: 0x6021a4
rvalue overload, n=2
address of make_unique::dummyvar: 0x6021a8
lvalue overload, n=1

关于引用template_type_dumper的警告表明,在第一个实例中,decltype(u) = int&&U = int,对于第二个decltype(u) = int&U = int&

很明显,正如

预期的那样有两个不同的实例,但她是我的问题:

  1. std::forward如何在这里工作?在第一个实例化中,它的模板参数是显式U = int的,它怎么知道它必须返回一个右值引用?如果我改为指定U&&会发生什么?
  2. make_unique声明为采用右值引用。为什么u可以成为左值参考?我缺少什么特殊规则吗?

make_unique被声明为采用右值引用。你怎么能成为左值参考?我缺少什么特殊规则吗?

make_unique声明为参考。这种参照是什么样的,有待推论。如果传递了类型 foo 的左值,则U被推导为foo&并且由于引用折叠规则U&&变得foo&(基本上,将一个右值引用与另一个引用"组合"总是产生一个右值引用;合并两个右值引用会产生一个右值引用(。如果传递 foo 类型的右值,则U被推导为 foo 并且U&& foo&&

这是支持完美转发的原因之一:使用U&&,您可以同时采用左值和右值,并推断出U以匹配适当的值类别。然后,使用 std::forward 您可以转发保留相同值类别的值:在第一种情况下,您得到转发左值的std::forward<foo&>,在第二种情况下,您将获得转发右值std::forward<foo>

在第一个实例化中,它的模板参数显式为 U = int,它怎么知道它必须返回一个 rvalue-reference?

因为std::forward<T>的返回类型始终是T&&。如果传递int则返回int&& 。如果传递int&则由于引用折叠规则,它将再次返回int&

如果我指定 U&& 会发生什么?

你会有std::forward<int&&>,引用折叠规则使int&& &&仍然是一个右值引用:int&& .

相关文章: