C++ | 完美转发:std::forward

发布时间 2023-09-22 16:54:18作者: C111-CR
  • 引用折叠
    template <class T>
    void func(T && arg);
    

    若一个右值引用(&&)参数被一个左值或左值引用初始化,那么引用将折叠为左值引用。(即:T&& & –> T&)

    int a = 1;
    func(a);		// func()中 参数arg 变成 int &类型
    int &ref = a;
    func(ref);		// func()中 参数arg 变成 int &类型
    

    若一个右值引用参数被一个右值初始化,那么引用将折叠为右值引用。(即:T&& && 变成 T&&)。

    func(10);		// func()中 参数arg 仍是 int &&类型
    

    一个左值引用参数不管是被一个左值还是右值初始化,引用都不能折叠,仍为左值引用(即:T& & –>T&,T& && –>T&)。

  • 为什么需要引入 std::forward ?

    有这么个例子:

    template <class T>
    void func(T && arg){
        cout << &arg;
    }
    

    这个函数是可以通过编译且运行的,并且在调用时无论传入一个左值(引用)或者右值,都不影响函数将参数arg视作左值并取其地址。

    这也就说明了,一个右值引用参数在函数中使用时,依旧会被当成一个左值去使用。

    另一个例子是:

    template <class T>
    Data* func(T && arg){
        return new Data(arg);
    }
    

    假定有一个类Data,这个类中实现了复制构造和移动构造函数,倘若使用以上写法,在函数func中,会调用Data的复制构造函数来初始化一个Data对象,而不是其移动构造函数。

    而使用std::forward就可以解决这个问题:

    template <class T>
    Data* func(T && arg){
        return new Data(std::forward<T>(arg));
    }
    

    将参数arg的右值引用属性保留并作为右值引用参数传给Data,所以称为完美转发。