Cpp 专题

RVO / NRVO

Return value optimzation 和 Named Return value optimzation

T make() {
  return T{}; // RVO
}
T make() {
  T a{};
  return a; // NRVO
}
T make(bool v) {
  T a{};
  T b{};
  if (v) {
    return a; // NRVO 通常做不了,可能退化为move constrcution
  }
  return b; // std::move
}

简单来说就是编译器会自动帮助我们做返回值优化,目标对象直接在最终位置构造,中间对象根本不产生,所以 copy/move constructor 都不会被调用。无论是在返回上构造还是返回一个已构造的值,不用去写以下类似的代码,当NRVO优化不了的时候,也会优先尝试move constructor,其实是 copy constructor。

return std::move(x);

std::move

std::move本质上是将一个value转化为右值引用,配合语境后才会具体调用move constructor / move assignment。

auto b = std::move(a); // move constructor
T c{};
c = std::move(b); // move assignment

std::move 在 std::array 和 std::vector 上的区别

std::array 的本质是一个数组,所以当对std::array做移动构造的时候,会逐个移动里面的元素,而std::vector结构本质是在管理指针,所以当移动构造的时候,会直接移交指针的控制权。

std::array<int, 10> a;
auto b = std::move(b); // 复制 10 个int
std::vector<int> a(10, 0);
auto b = std::move(a); // 复制三个指针

但是如果是std::array<std::string, N> 情况又会不一样,因为int本身是最基础的数据结构,所以移动构造和拷贝构造基本一样,但是如果类型是std::string的时候,std::array移动构造会直接调用元素的移动构造函数,虽然这个时候也是O(n)复杂度,但是移动构造和拷贝构造会有区别。

std::array<std::string, 10> a;
auto b = std::move(a); // 移交10个string的buffer
auto c = a; // 复制10个buffer


noexcept

声明一个函数是否是异常安全的,一个具体的例子是,当std::vector<T>扩容的时候,因为扩容的时候会考虑到异常安全,如果T的移动构造函数声明了 noexcept,这个时候会调用移动构造函数,当移动构造函数不是noexcept的时候,扩容的时候会考虑使用拷贝构造函数。

constexpr / consteval

一句话:

  • constexpr funciton: 编译和运行期都能用
  • consteval function: 只有编译能用
  • constexpr value: 必须编译期完成初始化,const类型
  • constinit value: 必须编译期完成初始化,不一定是cosnt类型

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注