Skip to the content.

07 创建对象时注意区分 () 和 {}

int a(0);
int b = 0;
int c{0};
int d = {0};  // 按 int d{0} 处理,后续讨论将忽略这种用法
A a;      // 默认构造
A b = a;  // 拷贝而非赋值
a = b;    // 拷贝而非赋值
std::vector<int> v{1, 2, 3};
struct A {
  int x{0};   // OK
  int y = 0;  // OK
  int z(0);   // 错误
};
double x = 1.1;
double y = 2.2;
int a{x + y};  // 错误:大括号初始化不允许 double 到 int 的收缩转换
int b(x + y);   // OK:double 被截断为 int
int c = x + y;  // OK:double 被截断为 int
struct A {
  A() { std::cout << 1; }
};

struct B {
  B(std::string) { std::cout << 2; }
};

A a();  // 不调用 A 的构造函数,而是被解析成一个函数声明:A a();
std::string s{"hi"};
B b(std::string(s));  // 不构造 B,被解析成函数声明 B b(std::string)
A a2{};               // 构造 A
B b2{std::string(s)};  // 构造 B

// C++11 之前的解决办法
A a3;
B b3((std::string(s)));
#include <iostream>
#include <string>

struct A {
  A(int) { std::cout << 1; }
  A(std::string) { std::cout << 2; }
  A(std::initializer_list<int>) { std::cout << 3; }
};

int main() {
  A a{0};  // 3
  // A b{3.14};  // 错误:大括号初始化不允许 double 到 int 的收缩转换
  A c{"hi"};  // 2
}
#include <iostream>

struct A {
  A() { std::cout << 1; }
  A(std::initializer_list<int>) { std::cout << 2; }
};

int main() {
  A a{};    // 1
  A b;  // 2
  A c({});  // 2
}
std::vector<int> v1(3, 6);  // 元素为 6、6、6
std::vector<int> v2{3, 6};  // 元素为 3、6
template <typename T, typename... Ts>
decltype(auto) f(Ts&&... args) {
  T x(std::forward<Ts>(args)...);  // 用小括号初始化创建临时对象
  return x;
}

template <typename T, typename... Ts>
decltype(auto) g(Ts&&... args) {
  T x{std::forward<Ts>(args)...};  // 用大括号初始化创建临时对象
  return x;
}

// 模板作者不知道调用者希望得到哪个结果
auto v1 = f<std::vector<int>>(3, 6);  // v1 元素为 6、6、6
auto v2 = g<std::vector<int>>(3, 6);  // v2 元素为 3、6
#include <iostream>
#include <vector>

int main() {
  auto p = std::make_shared<std::vector<int>>(3, 6);
  for (auto x : *p) {
    std::cout << x;  // 666
  }
}

08 用 nullptr 替代 0 和 NULL

#ifndef NULL
  #ifdef __cplusplus
    #define NULL 0
  #else
    #define NULL ((void *)0)
  #endif
#endif
#include <cstddef>

namespace jc {

constexpr int f(bool) { return 1; }
constexpr int f(int) { return 2; }
constexpr int f(void*) { return 3; }

static_assert(f(0) == 2);
static_assert(f(NULL) == 2);
static_assert(f(nullptr) == 3);

}  // namespace jc

int main() {}
template <typename T>
void f() {}

f(0);        // T 推断为 int
f(NULL);     // T 推断为 int
f(nullptr);  // T 推断为 std::nullptr_t
void f1(std::shared_ptr<int>) {}
void f2(std::unique_ptr<int>) {}
void f3(int*) {}

template <typename F, tpyename T>
void g(F f, T x) {
  f(x);
}

g(f1, 0);        // 错误
g(f1, NULL);     // 错误
g(f1, nullptr);  // OK

g(f2, 0);        // 错误
g(f2, NULL);     // 错误
g(f2, nullptr);  // OK

g(f3, 0);        // 错误
g(f3, NULL);     // 错误
g(f3, nullptr);  // OK

09 用 using 别名声明替代 typedef

using F = void (*)(int);  // typedef void (*F)(int)
template <typename T>
using Vector = std::vector<T>;  // Vector<int> 等价于 std::vector<int>

// C++11 之前的做法是在模板内部 typedef
template <typename T>
struct V {  // V<int>::type 等价于 std::vector<int>
  typedef std::vector<T> type;
};

// 在其他类模板中使用这两个别名的方式
template <typename T>
struct A {
  Vector<T> a;
  typename V<T>::type b;
};
template <typename T>
struct remove_reference {
  using type = T;
};

template <typename T>
struct remove_reference<T&> {
  using type = T;
};

template <typename T>
struct remove_reference<T&&> {
  using type = T;
};

template <typename T>
using remove_reference_t = typename remove_reference<T>::type;
template <typename T, tpyename U>
struct is_same {
  static constexpr bool value = false;
};

template <typename T>
constexpr bool is_same_v = is_same<T, U>::value;

10 用 enum class 替代 enum

enum X { a, b, c };
int a = 1;  // 错误:a 已在作用域内声明过
enum class X { a, b, c };
int a = 1;   // OK
X x = X::a;  // OK
X y = b;     // 错误
enum X { a, b, c };
X x = a;
if (x < 3.14) {  // 不应该将枚举与浮点数进行比较,但这里合法
}

enum class Y { a, b, c };
Y y = Y::a;
if (x < 3.14) {  // 错误:不允许比较
}
if (static_cast<double>(x) < 3.14) {  // OK:enum class 允许强制转换为其他类型
}
enum Color;    // C++11 之前错误
enum class X;  // OK
enum X { a, b, c };  // 编译器选择底层类型为 char
enum Status {        // 编译器选择比 char 更大的底层类型
  good = 0,
  failed = 1,
  incomplete = 100,
  corrupt = 200,
  indeterminate = 0xFFFFFFFF
};
enum class X : std::uint32_t;
// 也可以在定义中指定
enum class Y : std::uint32_t { a, b, c };
enum X { name, age, number };
auto t = std::make_tuple("downdemo", 6, "42");
auto x = std::get<name>(t);  // name 可隐式转换为 get 的模板参数类型 size_t
enum class X { name, age, number };
auto t = std::make_tuple("downdemo", 6, "13312345678");
auto x = std::get<static_cast<std::size_t>(X::name)>(t);
template <typename E>
constexpr auto f(E e) noexcept {
  return static_cast<std::underlying_type_t<E>>(e);
}

auto x = std::get<f(X::name)>(t);

11 用 =delete 替代 private 作用域来禁用函数

class A {
 private:
  A(const A&);  // 不需要定义
  A& operator(const A&);
};
class A {
 public:
  A(const A&) = delete;
  A& operator(const A&) = delete;
};
void f(int);
void f(double) = delete;  // 拒绝 double 和 float 类型参数

f(3.14);  // 错误
template <typename T>
void f(T x) {}

template <>
void f<int>(int) = delete;

f(1);  // 错误:使用已删除的函数
class A {
 public:
  template <typename T>
  void f(T x) {}
};

template <>
void A::f<int>(int) = delete;
class A {
 public:
  template<typename T>
  void f(T x) {}
 private:
  template<>
  void f<int>(int);
};

12 用 override 标记被重写的虚函数

namespace jc {

struct A {
  constexpr int f() & { return 1; }   // *this 是左值时才使用
  constexpr int f() && { return 2; }  // *this 是右值时才使用
};

constexpr A make_a() { return A{}; }

}  // namespace jc

int main() {
  jc::A a;
  static_assert(a.f() == 1);
  static_assert(jc::make_a().f() == 2);
}
struct A {
 public:
  virtual void f1() const;
  virtual void f2(int x);
  virtual void f3() &;
  void f4() const;
};

struct B : A {
  virtual void f1();
  virtual void f2(unsigned int x);
  virtual void f3() &&;
  void f4() const;
};
struct A {
  virtual void f1() const;
  virtual void f2(int x);
  virtual void f3() &;
  virtual void f4() const;
};

struct B : A {
  virtual void f1() const override;
  virtual void f2(int x) override;
  virtual void f3() & override;
  void f4() const override;
};
struct A {
  void override();  // 在 C++98 和 C++11 中都合法
};
struct A {
  virtual void f() final;
  void g() final;  // 错误:final 只能用于指定虚函数
};

struct B : A {
  virtual void f() override;  // 错误:f 不可重写
};
struct A final {};
struct B : A {};  // 错误:A 禁止被继承

13 用 std::cbeginstd::cend 获取 const_iterator

std::vector<int> v{2, 3};
auto it = std::find(std::cbegin(v), std::cend(v), 2);  // C++14
v.insert(it, 1);
template <typename C, typename T>
void f(C& c, const T& x, const T& y) {
  auto it = std::find(std::cbegin(c), std::cend(c), x);
  c.insert(it, y);
}
template <typename C>
auto cbegin(const C& c) -> decltype(std::begin(c)) {
  return std::begin(c);  // c 是 const 所以返回 const_iterator
}

14 用 noexcept 标记不抛异常的函数

int f(int x) throw();   // C++98
int f(int x) noexcept;  // C++11
RetType function(params) noexcept;  // most optimizable
RetType function(params) throw();   // less optimizable
RetType function(params);           // less optimizable
/*
 * 数组的 swap 由元素类型决定 noexcept 结果
 * 比如元素类型是 class A
 * 如果 swap(A, A) 不抛异常则该数组的 swap 也不抛异常
 */
template <typename T, size_t N>
void swap(T (&a)[N], T (&b)[N]) noexcept(noexcept(swap(*a, *b)));

// std::pair 的 swap
template <typename T, typename U>
struct pair {
  void swap(pair& p) noexcept(
      noexcept(swap(first, p.first)) && noexcept(swap(second, p.second)));
};
// 假设前置条件是 s.size() <= 32
void f(const std::string& s) noexcept;

15 用 constexpr 表示编译期常量

#define CPP98 199711L
#define CPP11 201103L
#define CPP14 201402L
#define CPP17 201703L
#define CPP20 202002L

// #if ((defined(_MSVC_LANG) && _MSVC_LANG > CPP11) || __cplusplus > CPP11)
// #define JC_HAS_CXX14
// #endif

#ifndef JC_HAS_CXX14
  #ifdef _MSVC_LANG
    #if _MSVC_LANG > CPP11
      #define JC_HAS_CXX14 1
    #else
      #define JC_HAS_CXX14 0
    #endif
  #else
    #if __cplusplus > CPP11
      #define JC_HAS_CXX14 1
    #else
      #define JC_HAS_CXX14 0
    #endif
  #endif
#endif  // JC_HAS_CXX14

namespace jc {

constexpr int pow(int base, int exp) noexcept {
#ifdef JC_HAS_CXX14
  auto res = 1;
  for (int i = 0; i < exp; ++i) {
    res *= base;
  }
  return res;
#else  // C++11 中,constexpr 函数只能包含一条语句
  return (exp == 0 ? 1 : base * pow(base, exp - 1));
#endif
}

}  // namespace jc

int main() {
  constexpr auto n = 4;
  static_assert(jc::pow(3, n) == 81);
}
auto base = 3;                    // 运行期获取值
auto exp = 10;                    // 运行期获取值
auto baseToExp = pow(base, exp);  // pow 在运行期被调用
namespace jc {

class Point {
 public:
  constexpr Point(double x = 0, double y = 0) noexcept : x_(x), y_(y) {}
  constexpr double x_value() const noexcept { return x_; }
  constexpr double y_value() const noexcept { return y_; }
  void set_x(double x) noexcept {
    x_ = x;  // 修改了对象所以不能声明为 constexpr
  }
  void set_y(double y) noexcept {  //  C++11 的 constexpr 函数不能返回 void
    y_ = y;
  }

 private:
  double x_;
  double y_;
};

constexpr Point midpoint(const Point& lhs, const Point& rhs) noexcept {
  return {(lhs.x_value() + rhs.x_value()) / 2,
          (lhs.y_value() + rhs.y_value()) / 2};
}

}  // namespace jc

int main() {
  constexpr jc::Point p1{1.1, 2.2};  // 编译期执行 constexpr 构造函数
  constexpr jc::Point p2{3.3, 4.4};  // 同上
  constexpr auto mid = jc::midpoint(p1, p2);
  static_assert(mid.x_value() == (1.1 + 3.3) / 2);
  static_assert(mid.y_value() == (2.2 + 4.4) / 2);
}
namespace jc {

class Point {
 public:
  constexpr Point(double x = 0, double y = 0) noexcept : x_(x), y_(y) {}
  constexpr double x_value() const noexcept { return x_; }
  constexpr double y_value() const noexcept { return y_; }
  constexpr void set_x(double x) noexcept { x_ = x; }
  constexpr void set_y(double y) noexcept { y_ = y; }

 private:
  double x_;
  double y_;
};

constexpr Point midpoint(const Point& lhs, const Point& rhs) noexcept {
  return {(lhs.x_value() + rhs.x_value()) / 2,
          (lhs.y_value() + rhs.y_value()) / 2};
}

constexpr Point reflection(const Point& p) noexcept {  // p 关于原点的对称点
  Point res;
  res.set_x(-p.x_value());
  res.set_y(-p.y_value());
  return res;
}

}  // namespace jc

int main() {
  constexpr jc::Point p1{1.1, 2.2};
  constexpr jc::Point p2{3.3, 4.4};
  constexpr auto mid = jc::midpoint(p1, p2);
  static_assert(mid.x_value() == (1.1 + 3.3) / 2);
  static_assert(mid.y_value() == (2.2 + 4.4) / 2);
  constexpr auto reflection_mid = jc::reflection(mid);
  static_assert(reflection_mid.x_value() == -mid.x_value());
  static_assert(reflection_mid.y_value() == -mid.y_value());
}

16 用 std::mutexstd::atomic 保证 const 成员函数线程安全

class Polynomial {
 public:
  std::vector<double> roots() const {
    if (!roots_are_valid_) {
      ... // 计算 root_vals_
      roots_are_valid_ = true;
    }
    return root_vals_;
  }

 private:
  mutable bool roots_are_valid_{false};
  mutable std::vector<double> root_vals_{};
};
class Polynomial {
 public:
  std::vector<double> roots() const {
    std::lock_guard<std::mutex> lk{m_};
    if (!roots_are_valid_) {
      ... // 计算 root_vals_
      roots_are_valid_ = true;
    }
    return root_vals_;
  }

 private:
  mutable std::mutex m_;
  mutable bool roots_are_valid_{false};
  mutable std::vector<double> root_vals_{};
};
class Point {
 public:
  double distance_from_origin() const noexcept {
    ++call_count_;  // 计算调用次数
    return std::sqrt((x_ * x_) + (y_ * y_));
  }

 private:
  mutable std::atomic<unsigned> call_count_{0};
  double x_;
  double y_;
};
class A {
 public:
  int f() const {
    if (flag_) {
      return res_;
    } else {
      auto x = expensive_computation1();
      auto y = expensive_computation2();
      res_ = x + y;
      flag_ = true;  // 设置标记
      return res_;
    }
  }

 private:
  mutable std::atomic<bool> flag_{false};
  mutable std::atomic<int> res_;
};
class A {
 public:
  int f() const {
    if (flag_) {
      return res_;
    } else {
      flag_ = true;  // 在计算前设置标记值为 true
      auto x = expensive_computation1();
      auto y = expensive_computation2();
      res_ = x + y;
      return res_;
    }
  }

 private:
  mutable std::atomic<bool> flag_{false};
  mutable std::atomic<int> res_;
};
class A {
 public:
  int f() const {
    std::lock_guard<std::mutex> lk{m_};
    if (flag_) {
      return res_;
    } else {
      auto x = expensive_computation1();
      auto y = expensive_computation2();
      res_ = x + y;
      flag_ = true;
      return res_;
    }
  }

 private:
  mutable std::mutex m_;
  mutable bool flag_{false};
  mutable int res_;
};

17 特殊成员函数的隐式合成与抑制机制

struct A {
  A(A&& rhs);             // 移动构造函数
  A& operator=(A&& rhs);  // 移动赋值运算符
};
struct A {
  virtual ~A() = default;
  A(A&&) = default;
  A& operator=(A&&) = default;
  A(const A&) = default;
  A& operator=(const A&) = default;
};
class StringTable {
 private:
  std::map<int, std::string> values_;
};
class StringTable {
 public:
  StringTable() { makeLogEntry("Creating StringTable object"); }
  ~StringTable() { makeLogEntry("Destroying StringTable object"); }

 private:
  std::map<int, std::string> values_;
};
struct A {
  template <typename T>
  A(const T& rhs);  // 从任意类型构造

  template <typename T>
  A& operator=(const T& rhs);  // 从任意类型赋值
};