C++ 中文周刊 第136期

周刊项目地址

公众号

qq群 手机qq点击进入

RSS https://github.com/wanghenshui/cppweeklynews/releases.atom

欢迎投稿,推荐或自荐文章/软件/资源等

提交 issue

感谢 不语 赞助

最近在找工作准备面试题,更新可能有些拖沓,见谅

以后可能要改变一下内容

一周发文章总结,一周发视频总结,这样两种内容交叉一下

本期发视频总结


资讯

标准委员会动态/ide/编译器信息放在这里

编译器信息最新动态推荐关注hellogcc公众号 OSDT Weekly 2023-10-25 第225期

视频

CPPNorth去年的讲一下。今年的PPT没全放出来

Amir Kirsh - Are your Structures Bindable

怎么检测一个类型是否能结构化展开?structure_bindale?

这也是一个SO上的提问 https://stackoverflow.com/questions/63369361/how-to-define-a-concept-of-a-object-that-is-can-be-structured-binding

代码在 https://godbolt.org/z/ocox9sqed

不知道什么场景能用上。看一乐

Amir Kirsh - The fine details behind C++ containers and algorithms

一些容器使用上的经验/坑

vector push_back的T有三种场景

std::copy 可能memmove 可能for循环无优化,也有alias问题,不重叠会优化成memcpy 感谢吴乎指出.注意使用

list原地sort通常不如拷贝到vector再sort 数据局部性问题

开头插入,list还是vector都不如 reserve预留空间 + push_back + 反转 reverse 快

尽可能降低挪动

https://quick-bench.com/q/Cx35L5th0bsvDMVHCdK61g08_z4 这个思路还是挺有意思的

群友anien指出,测试数据/结论不可靠,明显受cache

我加上pad做了测试,发现确实 list还是快一些的https://quick-bench.com/q/JDIZFmwrlmqJwqCENhWLFEOhhC0

还是要考虑数据集的。感谢anien指正

按照index下标erase vector的优化,可以通过move来代替erase,最后只erase一次

这里index是降序的

size_t removed = 0;
for(auto index: indices_to_erase) { // indices_to_erase are sorted in descending order
  std::move(vec.begin() + index + 1, vec.end() - removed, vec.begin() + index);
  ++removed;
}
vec.erase(vec.end() - removed, vec.end());

降低交换次数,move相当于交换+移动,减少拷贝,删除的越多,收益越大

如果index数组是升序的,怎么写?

unordered_map的优化

尽可能用insert_or_assign,比赋值快

https://stackoverflow.com/questions/63041408/unordered-map-excess-calls-to-hash-function

计算hash过于频繁

我觉得还是别用这破玩意了

try_emplace提高性能,这个之前咱们也提过,但一定要用对,不然可能性能下降。或者装作不知道这个函数吧

另外删改写回场景c++17可以通过extract node insert node实现,性能更好一些

https://quick-bench.com/q/QtFK3ZJSXuf53l82e_z96Mq8fyk

range尽可能使用

constexpr std::string_view words{"Hello-_-C++-_-20-_-!"};
constexpr std::string_view delim{"-_-"};
for (const auto word : std::views::split(words, delim)) {
  std::cout << std::quoted(std::string_view(word.begin(), word.end())) << ' ';
}

或者用 https://zh.cppreference.com/w/cpp/ranges/lazy_split_view

为啥还有lazy版本?难道split_view不是lazy的?

并不是,主要原因是split_view比较难用 https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2210r2.html

不展开了跑题了朋友们

Dean Moldovan - A std-fs-path from bug to patch

std::fs::path 不是unicode-safe的

特殊字母会有问题,想要用utf-8,可以用std::fs::u8path

他们不会搞定制clang-tidy规则,改系统源代码,给std::fs::path加deprecate信息。。。

Timur Doumler - C++ Lambda Idioms

lambda转函数指针

int main() {
  auto f = +[](int i){ return i * i; };
  static_assert(std::is_same_v<decltype(f), int(*)(int)>);
}

怎么立即执行lambda?加括号?有没有更好的写法?invoke

捕获初始化优化代码

比如这种

const std::vector<std::string> vs = {"apple", "orange", "foobar", "lemon"};
const std::string prefix = "foo";
auto result = std::find_if(
    vs.begin(), vs.end(),
    [&prefix](const std::string& s) {
        return s == prefix + "bar";
    });
if (result != vs.end())
    std::cout << prefix << "-something found!\n";

这个prefix的构造就很脑瘫,怎么写更合理?把”bar”放外面?如果”bar”没法放外面只能这么构造怎么办?

const std::vector<std::string> vs = {"apple", "orange", "foobar", "lemon"};
const std::string prefix = "foo";
auto result = std::find_if(
    vs.begin(), vs.end(),
    [str = prefix + "bar" ](const std::string& s) {
        return s == str;
    });
if (result != vs.end())
    std::cout << prefix << "-something found!\n";

通过捕获的构造来替换内部的构造

lambda递归调用

常规

int main() {
  std::function<int(int)> f = [&](int i) {
    if (i == 0) return 1;
    return i * f(i - 1);
  };
  std::cout << f(5); // prints 120
}

function有类型擦除开销,有没有其他方案?Y组合子?

int main() {
  auto f = [&](auto&& self, int i) {
    if (i == 0) return 1;
    return i * self(self, i - 1);
  };
  auto recursive = [](auto&& f, auto&&... args) {
    return f(f, std::forward<decltype(args)>(args)...);
  };
  std::cout << recursive(f, 5); // prints 120
}

c++23的deducing this可以很好的写出来

int main() {
  auto f = [&](this auto&& self, int i){
    if (i == 0) return 1;
    return i * self(i - 1);
  };
  std::cout << f(5); // prints 120
}

这玩意结合overload惯用法可以有更花的玩法

struct Leaf {};
struct Node;
using Tree = std::variant<Leaf, Node*>;
struct Node {
  Tree left, right;
};

template <typename... Ts>
struct overload : Ts... { using Ts::operator()...; }

int countLeaves(const Tree& tree) {
  return std::visit(overload{
      [] (const Leaf&) { return 1; },
      [] (this const auto& self, const Node* node) -> int {
        return visit(self, node->left) + visit(self, node->right);
      }
    }, 
    tree);
}

其他

P99 CONF 2023 | Adventures in Thread-per-Core Async with Redpanda and Seastar by Travis Downs

https://www.bilibili.com/video/BV1gg4y1d7jB/

redpanda有一些coroutine实践,有点意思

seastar是thread per core 消息传递 share nothing,异步的处理阻塞非常重要

原来的seaster是传递future continuation的,引入couroutine就可以把then链切成co_await

但引入coroutine也是有代价的

小的任务,不建议coroutine,能同步就同步,不能同步再then链,不能then链再coroutine

这个也是经验之谈,目前没有代码复现一下。群友anien指出llvm可能优化掉

开源项目需要人手

看到这里或许你有建议或者疑问或者指出错误,请留言评论! 多谢! 你的评论非常重要!也可以帮忙点赞收藏转发!多谢支持! 觉得写的不错那就给点吧, 在线乞讨 微信转账