X

曜彤.手记

随记,关于互联网技术、产品与创业

吉 ICP 备10004938号

《C++17 STL Cookbook》读书笔记(一)


看到知乎上有人推荐的一本书。

Chapter 1: The New C++17 Features

结构化绑定(Structured Bindings / Unpacking)
std::pair<int, int> map(int x, int y) {
  return { x, y };
}
struct City {
  int id;
  std::string label;
};
int main(int argc, char **argv) {
  // std::pair.
  const auto [x, y] = (map(10, 20));
  std::cout << x << y << std::endl;

  // std::tuple.
  const auto [age, name] = std::make_tuple<>(27, std::string("YHSPY"));
  std::cout << name << age << std::endl;

  // struct (with reference).
  City city = { 10, "Shanghai" };
  const auto &[id, label] = city;
  std::cout << id << label << std::endl;

  // array.
  int arr[]= {1, 2};
  const auto [first, second] = arr;

  // std::array.
  auto [m, n] = std::array<int, 2>({10, 20});
  return 0;
}

一些说明

For each Ti in Types…, the corresponding type Vi in VTypes… is std::decay<Ti>::type unless application of std::decay results in std::reference_wrapper<X> for some type X, in which case the deduced type is X&.

int main(int argc, char **argv) {
  int x {};
  int y {};
  /**
   * template< class T, class... Types >
   * constexpr T&& get(tuple<Types...>&& t) noexcept;
   */
  // decay.
  const auto [v] = std::make_tuple<int&&>(std::move(x));
  static_assert(std::is_same_v<decltype(v), const int>);
  // no decay.
  const auto [c] = std::tuple<int&&>(std::move(y));
  static_assert(std::is_same_v<decltype(c), int&&>);
  return 0;
}
const auto [v] = std::tuple<int&>(x);
/**
 * 等价于:
 * const auto e = std::tuple<int&>(x);  // const -> std::tuple;
 * auto&& r = std::get<0>(std::move(e));  // auto&& T -> &/&&;
 * (introduce v as a name for that which r refers to)
 */
if 与 switch 的语句内初始化(语法糖)
int main(int argc, char **argv) {
  if (int x = 10; x == 10) {
    std::cout << "Yes!" << std::endl;
  }
  /*
      {
        int x = 10;
        if (x == 10) {
          std::cout << "Yes!" << std::endl;
        }
      }
   */
  switch(int y = 10; y) {
    case 10: {
      std::cout << "Yes again!" << std::endl;
    }
    default: {}
  }
  return 0;
}

应用场景

新的括号初始化规则
int main(int argc, char **argv) {
  // "x" should be the type of the only element inside the brackets in C++17.
  auto x {1}; 
  auto y = {1, 2, 3};  // y -> std::initializer_list.
  // auto z {1, 2}; // illegal.
  std::cout << typeid(x).name() << std::endl;
  return 0;
}
基于构造函数的自动模板类型推导(CTAD)
int main(int argc, char **argv) {
  std::pair city(1, "Shanghai");
  std::tuple scores(4, 3, 2.5);

  return 0;
}

使用 std::common_type_t 让编译器选择合适的推导类型。

template <typename T>
struct sum {
    T value;
    template <typename ...Ts>
    sum(Ts&& ...values) : value{(values + ...)} {}
};

template <typename ...Ts>
sum(Ts&& ...ts) -> sum<std::common_type_t<Ts...>>;
int main(int argc, char **argv) {
  sum s {1u, 2.0, 3, 4.0f};
  std::cout << s.value << std::endl;  // 10.
  return 0;
}
constexpr-if 的编译时决策
template <typename T>
class Addable { 
  T val;
public:
  Addable(T v) : val{v} {}
  template <typename U>
  T add(U x) const {
    if constexpr (std::is_same_v<T, std::vector<U>>) {  // evaluting at compile-time.
      auto copy(val);
      for (auto &n : copy) { 
        n += x;
      }
      return copy;
    } else {
      return val + x;
    }
  }
};
int main(int argc, char **argv) {
  Addable addable {std::vector<int>{1, 2, 3}};
  for (const auto& v : addable.add(100)) {
    std::cout << v << std::endl;
  }
  return 0;
}

一些说明

全局变量内联
struct A {
  static const inline std::string name = "YHSPY";
};
inline A a;
int main(int argc, char **argv) {
  std::cout << a.name << std::endl;
  return 0;
}

一些说明

折叠表达式

可用于方便地对变长参数模板进行参数解包

// before C++11:
template<typename ...T> double mul(T ...args);  // 入口;
template<typename S, typename ...T> double mul(S arg, T ...args) { return arg * mul<T...>(args...); }  // 递归循环体;
template<> double mul() { return 1; }  // 边界条件(全特化);
// after C++17:
template<typename ...Ts> double mulByFold(Ts ...args) {
  return (args * ... * 1);  // 折叠表达式(二元折叠);
}
int main(int argc, char **argv) {
  std::cout << mul<>(1, 2, 3, 4) << std::endl;  // 24.
  std::cout << mulByFold<>(1, 2, 3, 4) << std::endl;  // 24.
  return 0;
}

场景 1:查询给定参数在容器中总共出现的次数:

template <typename R, typename ...Ts>
auto matches(const R& range, Ts ...ts) {
  return (std::count(std::begin(range), std::end(range), ts) + ...);
}
int main(int argc, char **argv) {
  std::cout << matches(std::vector{1, 2, 2, 3, 4, 5}, 2, 5); 
  return 0;
}

场景 2:检查顺序的批量元素容器插入操作是否成功:

template <typename T, typename ...Ts>
bool insertAllToSet(T&& set, Ts ...ts) {
  return (set.insert(ts).second && ...);  // short-circuit.
}
int main(int argc, char **argv) {
  std::cout << std::boolalpha << insertAllToSet(std::set<int>{1, 2}, 3, 4, 5);
  return 0;
}

场景 3:检查是否所有元素均在给定的范围内:

template <typename T, typename ...Ts>
bool within(T min, T max, Ts ...ts) {
  return ((min <= ts && ts <= max) && ...);
}
int main(int argc, char **argv) {
  std::cout << std::boolalpha << within(1, 10, 1.3, 2, 3);
  return 0;
}

场景 4:批量向容器插入元素:

template <typename T, typename ...Ts>
void insertAllToVector(std::vector<T>& vec, Ts ...ts) {
  (vec.push_back(ts), ...);  // comma expression.
}
int main(int argc, char **argv) {
  std::vector<int> vec {};
  insertAllToVector(vec, 1, 2, 3);
  std::cout << vec.size() << std::endl;
  return 0;
}

Chapter 2: STL Containers

std::vector 与 Erase-remove 惯用法

int main(int argc, char **argv) {
  std::vector v {1, 2, 3, 2, 5, 2, 6, 2, 4, 8};
  const auto newEnd (std::remove(std::begin(v),std::end(v), 2));  // remove.
  v.erase(newEnd, end(v));  // erase.

  // predicate version:
  const auto odd ([](int i) { return i % 2 != 0; });
  v.erase(std::remove_if(std::begin(v), std::end(v), odd), std::end(v));  // remove && erase.
  v.shrink_to_fit();  // shrink capacity of the container.
  for (const auto& i : v) { std::cout << i << std::endl; }
  return 0;
}
从未排序的 std::vector 中快速删除元素
// index version:
template <typename T>
void quickRemoveAt(std::vector<T>& v, std::size_t idx) {
  if (idx < v.size()) {
    v[idx] = std::move(v.back());  // use std::move rather than copy.
    v.pop_back();  // discard the last item.
  }
}
// iterator version:
template <typename T>
void quickRemoveAt(std::vector<T>& v, typename std::vector<T>::iterator it) {
  if (it != std::end(v)) {
    *it = std::move(v.back());
    v.pop_back();
  }
}
int main(int argc, char **argv) {
  std::vector vec {1, 2, 3};
  quickRemoveAt(vec, 1);
  for (const auto& v : vec) {
    std::cout << v << std::endl;
  }
  return 0;
}
以最快 / 最安全的方式访问 std::vector / std::array
保持 std::vector / std::array 以有序的方式插入元素
void insertSorted(std::vector<int>& v, int num) {
  // find the first element which is the greater or equal than the given one.
  const auto insertPos(std::lower_bound(std::begin(v), std::end(v), num));  
  // insert the new element just behind this one.
  v.insert(insertPos, num);
}
int main(int argc, char **argv) {
  std::vector vec {1, 2, 3, 4, 5, 6};
  insertSorted(vec, 2);
  assert(true == std::is_sorted(std::begin(vec), std::end(vec)));
  return 0;
}
高效和选择性地插入元素到 std::map / std::unordered_map
int main(int argc, char **argv) {
  std::map<int, std::string> m { { 1, "YHSPY" }, { 2, "SKY" } };
  auto [iter, success] = m.try_emplace(2, "SUN");
  if (!success) {
    iter->second = "SUM";
  }
  for (const auto& [key, value] : m) {
    std::cout << value << std::endl;
  }
  return 0;
}
std::map::insert 的新插入提示语义(性能优化)

int main(int argc, char **argv) {
  std::map<std::string, size_t> m { { "b", 1 }, { "c", 2 }, { "d", 3 } };
  auto insertIt(std::end(m));
  for (const auto &s : { "z", "y", "x", "w" }) {
    // hint (point to the existing element, which is greater than the element to be inserted).
    insertIt = m.insert(insertIt, { s, 1 });
  }
  return 0;
}
高效地修改 std::map 中元素的 Key
int main(int argc, char **argv) {
  std::map<int, std::string> m {
    { 1, "Mario" }, { 2, "Luigi" }
  };
  auto a (m.extract(1));  // extract the target node.
  auto b (m.extract(2));
  if (!a.empty() && !b.empty()) {
    std::swap(a.key(), b.key());  // get nonconst access to the key.
    m.insert(std::move(a));  // no "move" or "copy", just adjust the internal pointer of each "node_type";
    m.insert(std::move(b));
    for (const auto& [key, value] : m) {
      std::cout << key << value << std::endl;
    }
  }
  return 0;
}
在 std::unordered_map 中使用自定义类型做 Key
struct Name {
  std::string val;
};
bool operator==(const Name& x, const Name& y) {
  return x.val == y.val;
}
template<> struct std::hash<Name> {  // specialization for std::hash<Name>;
  size_t operator()(const Name& ins) const {
    return std::hash<std::string>()(ins.val);
  }
};
int main(int argc, char **argv) {
  std::unordered_map<Name, int> um {
    {{"Alice"}, 1}, {{"YHSPY"}, 2}
  };
  return 0;
}
使用 std::set 过滤重复的用户输入并排序
int main(int argc, char **argv) {
  std::set<std::string> s;
  std::istream_iterator<std::string> it { std::cin };
  std::istream_iterator<std::string> end;
  std::copy(it, end, std::inserter(s, s.end()));
  return 0;
}
使用 std::priority_queue 编写 TODO 应用
int main(int argc, char **argv) {
  using tItem = std::pair<int, std::string>;
  std::priority_queue<tItem> todo;
  auto il = { 
    tItem{0, "read comics"},
    tItem{2, "do homework"},
    tItem{1, "dishes"},
  };
  for (const auto& p : il) {
    todo.push(p);
  }
  while (!todo.empty()) {
    std::cout << todo.top().first << ": " << todo.top().second << std::endl;
    todo.pop();
  }
  return 0;
}


这是文章底线,下面是评论
  暂无评论,欢迎勾搭 :)