Skip to the content.

41 对于可拷贝的形参,如果移动成本低且一定会被拷贝则考虑传值

class A {
 public:
  void f(const std::string& s) { v_.push_back(s); }
  void f(std::string&& s) { v_.push_back(std::move(s)); }

 private:
  std::vector<std::string> v_;
};
class A {
 public:
  template <typename T>
  void f(T&& s) {
    v_.push_back(std::forward<T>(s));
  }

 private:
  std::vector<std::string> v_;
};
class A {
 public:
  void f(std::string s) { v_.push_back(std::move(s)); }

 private:
  std::vector<std::string> v_;
};
A a;
std::string s{"hi"};
a.f(s);     // 以传左值的方式调用
a.f("hi");  // 以传右值的方式调用
class A {
 public:
  void f(std::unique_ptr<std::string>&& p) { p_ = std::move(p); }

 private:
  std::unique_ptr<std::string> p_;
};
class A {
 public:
  void f(std::unique_ptr<std::string> p) { p_ = std::move(p); }

 private:
  std::unique_ptr<std::string> p_;
};
class A {
 public:
  void f(std::string s) {
    if (s.size() <= 15) {
      v_.push_back(std::move(s));  // 不满足条件则不添加,但比传引用多了一次析构
    }
  }

 private:
  std::vector<std::string> v_;
};
#include <string>
#include <utility>

class A {
 public:
  explicit A(std::string s) : s_(std::move(s)) {}
  void f(std::string s) { s_ = std::move(s); }

 private:
  std::string s_;
};

int main() {
  std::string s{"hello"};
  A a(s);

  std::string x{"hi"};
  /*
   * 额外的分配和回收成本,可能远高于 std::string 的移动成本
   * 传引用则不会有此成本,因为现在 a.s 的长度比之前小
   */
  a.f(x);

  std::string y{"hello world"};
  a.f(y);  // a.s 比之前长,传值和传引用都有额外的分配和回收成本,开销区别不大
}

42 用 emplace 操作替代 insert 操作

template <typename T, class Allocator = allocator<T>>
class vector {
 public:
  void push_back(const T& x);
  void push_back(T&& x);
};
push_back => emplace_back     // std::list、std::deque、std::vector
push_front => emplace_front   // std::list、std::deque、std::forward_list
insert_after => emplace_after // std::forward_list
insert => emplace             // 除 std::forward_list、std::array 外的所有容器
insert => try_emplace         // C++17,std:map、std::unordered_map
emplace_hint                  // 所有关联容器
std::vector<std::string> v;
std::string s{"hi"};
// 下面两个调用的效果相同
v.push_back(s);
v.emplace_back(s);
std::vector<std::string> v{"hhh", "iii"};
v.emplace(v.begin(), "hi");  // 创建一个临时对象后移动赋值
#include <chrono>
#include <functional>
#include <iomanip>
#include <iostream>
#include <set>
#include <tuple>

class A {
 public:
  A(int a, int b, int c) : a_(a), b_(b), c_(c) {}
  bool operator<(const A &rhs) const {
    return std::tie(a_, b_, c_) < std::tie(rhs.a_, rhs.b_, rhs.c_);
  }

 private:
  int a_;
  int b_;
  int c_;
};

constexpr int n = 100;

void set_emplace() {
  std::set<A> set;
  for (int i = 0; i < n; ++i) {
    for (int j = 0; j < n; ++j) {
      for (int k = 0; k < n; ++k) {
        set.emplace(i, j, k);
      }
    }
  }
}

void set_insert() {
  std::set<A> set;
  for (int i = 0; i < n; ++i) {
    for (int j = 0; j < n; ++j) {
      for (int k = 0; k < n; ++k) {
        set.insert(A(i, j, k));
      }
    }
  }
}

void test(std::function<void()> f) {
  auto start = std::chrono::system_clock::now();
  f();
  auto stop = std::chrono::system_clock::now();
  std::chrono::duration<double, std::milli> time = stop - start;
  std::cout << std::fixed << std::setprecision(2) << time.count() << " ms\n";
}

int main() {
  test(set_insert);
  test(set_emplace);
  test(set_insert);
  test(set_emplace);
  test(set_insert);
  test(set_emplace);
}
std::list<std::shared_ptr<A>> v;

void f(A*);
v.push_back(std::shared_ptr<A>(new A, f));
// 或者如下,意义相同
v.push_back({new A, f});
std::shared_ptr<A> p(new A, f);
v.push_back(std::move(p));
// emplace_back 的写法相同,此时两者开销区别不大
v.emplace_back(std::move(p));
std::vector<std::regex> v;
v.push_back(nullptr);  // 编译出错
v.emplace_back(nullptr);  // 编译通过,运行时抛出异常,难以发现此问题
std::regex r = nullptr;  // 错误
std::regex r{nullptr};  // 能编译但会引发异常,未定义行为