跳过正文

引用折叠+万能引用+std::forward == 完美转发

··760 字·2 分钟·
Advance C++ Modern C++ Program
作者
Samuka007
Do it well.
目录

引入
#

C++作为一门兼顾优雅与效率的语言,自然不会让你幸苦写几遍代码只为了辨析复制和移动~

所以,当你再遇到像

void insert ( const T& value );
void insert ( T&& value );

这样要重载两次的版本时,倘若他们之间除了形参不同,没有其他不同的话,你大可以使用 完美转发 来帮你优雅地省下复制粘贴的时间~(不是

前置知识
#

要想使用 完美转发,你需要了解:

如果你对于上述名词感到非常陌生,对于移动构造、移动语义也不太了解,可以先了解基础知识:

Demo
#

其实在cppreference中,已经讲述了 完美转发 的原理了(见 引用声明 - 转发引用)。 这里通过一个demo来演示完美转发的基本使用,你也可以点此前往 Compiler Explorer 试一试。

Talk is cheap. Show me the code. Linus Torvalds

#include    <cstdio>
#include    <utility>

using namespace std;

struct X {
    X()             { puts("X()"); }
    virtual ~X()    { puts("~X()"); }

    // copy constructor
    // X&, const X& and const X&& will call this
    X( const X& )   { puts("X( const X& )"); } 

    // move constructor
    // X&& will call this
    X( X&& )noexcept{ puts("X( X&& )"); }
};

// here Ty can be (const) X and X&
// if Ty == X, Ty&& == X&&
// if Ty == X&, Ty&& == X& && == X&
template < typename Ty > 
void construct ( Ty&& x ) {
    // in order to call constructor instead of generate a reference,
    // here use 'decay_t<Ty>' instead of 'auto'
    // you actually can write it like
    // decay_t<decltype(x)> newX ( forward<Ty>(x) );
    // which is a much more complex way to do the same thing
    decay_t<Ty> newX ( forward<Ty>(x) );
}

int main() {
    X x;
    puts("------");
    construct ( x ); // newX is a copy of x
    puts("------");
    construct ( move(x) ); // newX is move from x
    puts("------");
    // x call ~X()
}

或者,在C++20中,你也可以综合auto的形参推导和decltype来进一步省略:

void construct ( auto&& x ) {
    auto newX ( forward<decltype(x)>(x) );
}

运行结果:

x86-64 gcc 13.2 -std=c++20
Program returned: 0
Program stdout
X()
------
X( const X& )
~X()
------
X( X&& )
~X()
------
~X()

显然,construct()在传入x时,调用复制构造(形参类型为X&);在传入move(x)时,调用移动构造(形参类型为X&&),理想地实现了完美转发。

什么时候可以 auto auto ( auto auto ) { auto; }

参考
#

在这里你也可以找到关于万能引用、引用折叠与完美转发的关系:现代C++之万能引用、完美转发、引用折叠 - Francis - 知乎