Skip to main content

Command Palette

Search for a command to run...

C++11中的完美转发

Published
C++11中的完美转发

背景问题

我们想要创建创建一个工厂模版函数factory, 使用Arg类型的参数arg,构造一个T对象,并返回它的shared_ptr, 我们的理想是factory就像不存在一样,因此我们需要一个完美转发(perfect forwarding):

  • 如果参数是 左值引用,直接以 左值引用 的形式,转发给下一个函数
  • 如果参数是 右值引用,要先 “还原” 为 右值引用 的形式,再转发给下一个函数

解决方案

完美转发的两个特定,对应着两个要求

  1. 必须按引用传递;且左值和右值必须都能引用
  2. 若引用右值,必须保留右值的特征

对于第一点,通用引用可以完成。 对于第二点,通用引用后,无论是左值引用还是右值引用,他们都是左值,对于左值引用直接转发就可以,但是对于右值引用,我们需要使用static_cast将它转化为一个右值后转发。

函数定义

template<typename T, typename Arg>
std::shared_ptr<T> factory(Arg&& arg) {
    return std::shared_ptr<T>(new T(forward<Arg>(arg)));
}

template<typename S>
S&& forward(typename std::remove_reference<S>::type& s) noexcept {
    return static_cast<S&&>(s);
}

假设我们有一个类Foo, 拥有一个构造函数Foo(int n)

传递左值

// 当int x = 10; factory<Foo>(x) 传递一个左值时
// Arg&& arg是通用引用, Arg被推导为 int&
std::shared_ptr<Foo> factory(int& arg) {
    return std::shared_ptr<Foo>(new Foo(forward<int&>(arg)));
}

// 对于 forward<int&>(arg)
// S被推导为int&
int& && forward(int& s) noexcept {
    return static_cast<int& &&>(s);
}

// 进行引用折叠
int& forward(int& s) noexcept {
    return static_cast<int&>(s)
}

可以看出:当左值arg引用的是左值时,传递给new Foo()的是被引用的左值(被引用的左值再经过static_cast<Foo&>得到的还是左值引用)。

传递右值

// 当 factory<Foo>(10), 传递一个右值时
// Arg&& 是通用阴影, Arg被推导为 int
std::shared_ptr<Foo> factory(int&& arg) {
    return std::shared_ptr<Foo>(new Foo(forward<int>(arg)));
}

// 对于 forward<int>(arg)
// S被推导为 int
int&& forward(int& s) noexcept {
    return static_cast<int&&>(s);
}

可以看出:当左值arg引用的是右值时,传递给new T()的是被引用的右值(static_cast把arg强制为右值,因为没有名字)。

download.jpg

Ref

  1. https://www.yuanguohuo.com/2018/05/25/cpp11-perfect-forward/

More from this blog

<Programming with Types>随想: Chapter 2. Basic types

类型限制了一个变量可以接受的有效值的集合,对数据可以进行的操作,数据的意义。 空类型(The empty type) 根据类型的定义,类型定义了可以接受的有效值集合,那么这个集合有没有可能为空?答案是有可能的,TypeScirpt 的 never 就是这种类型。 需要注意的是,空类型不同与 void, 后者是有效值集合当中只有一个值,但这个值没有任何意义。而空类型的有效值集合本身是空的。 使用场景 控制流分析 在函数调用时,标志一个函数不会返回任何值: 在调用过程中抛出异常、死循环或者程序崩溃...

Mar 5, 2023
<Programming with Types>随想: Chapter 2. Basic types

精读《设计机器学习系统》-ch04: 训练数据

不同于 Chapter03 从系统的角度来处理数据,这一章从数据科学的视角来处理数据。这章的标题是“training Data”,而非“training dataset”,因为 数据集(dataset) 意味着有限(finite)和固定(stationary), 而现实生产环境中的 数据(data) 通常是 无限 并且 不固定 的。 抽样 抽样方法在 ML 项目的生命周期中无处不在,在这一节中,我们使用生成训练数据作为例子。 那为什么需要抽样?直接使用全部数据不可以吗? 首先,在现实世界中,并不...

Dec 10, 2022
精读《设计机器学习系统》-ch04: 训练数据
7

70 Talk

19 posts