1

这个关于用法的例子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&&and U = int,对于第二个decltype(u) = int&and U = int&

很明显,正如预期的那样,有两种不同的实例化,但她是我的问题:

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

1 回答 1

4

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,它怎么知道它必须返回一个右值引用?

因为返回类型std::forward<T>总是T&&。如果你通过int它返回int&&。如果你通过它,它会因为引用折叠规则而再次int&返回。int&

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

您将拥有std::forward<int&&>并且引用折叠规则int&& &&仍然会生成右值引用:int&&.

于 2012-04-30T20:26:20.627 回答