What and Why 这次单独说一下 std::exchange,它是 C++14 <utility> 提供的一个函数模板,实现很简单。 template<class T, class U = T> constexpr // since C++20 T exchange(T& obj, U&& new_value) noexcept( // since C++23 std::is_nothrow_move_constructible<T>::value && std::is_nothrow_assignable<T&, U>::value ) { T old_value = std::move(obj); obj = std::forward<U>(new_value); return old_value; } 看实现可知其逻辑很简单,就是设置新值、返回旧值。但却难以顾名思义,它实际上并不会交换数据,那得这样写: y = std::exchange(x, y); 上篇说过,std::exchange 和… Continue Reading std::exchange use cases

今天讲一个 Idiom 加一些 Tricks。 本次内容紧紧围绕着 The Rule of the Big Five,即 destructor copy constructor copy assignment constructor move constructor move assignment constructor 要讲的 Idiom 主要是针对后三个,Tricks 则主要是针对后两个,属于是一些额外的优化技巧。 先从 The Big 3 说起,就是前三个,由此引出 Idiom 存在的必要性。一个小例子: class my_string { public: // default ctor my_string(const char* str = "") : size_ { std::strlen(str) }… Continue Reading T230723 copy-and-swap Idiom and More Tricks

本次谈逻辑分派。 三种最基本的逻辑关系为相似、承接和因果关系。 逻辑分派主要指的是因果关系,因果关系里面又包含条件关系。因果关系是一种非常特殊且重要的关系,表示两个功能块之间具备极强的依赖性。功能 B 只有依赖于功能 A 才能存在,彼此不能分开。 像简单功能模块使用的 if-else,constexpr if, Parametric Polymorphism 当中使用的 Partial Specialization/Tag dispatching/SFINAE/Concepts,Ad hoc Polymorphism 当中使用的 overload resolution,还有 Subtyping Polymorphism 当中使用的 virtual function,都属于逻辑分派。 结构从本质上来说,是一种逻辑。程序结构本质是程序的逻辑体现,混乱的逻辑代表混乱的结构,清晰的逻辑代表清晰的结构,结构越简洁、越清晰,程序越具有稳定性,扩展维护起来也比较轻松。 早期的这些逻辑分派,大多依赖于 type,反而是最朴素的 if-else 依赖于 value。依赖 type 往往需要定义很多的类型,甚至很简单的功能也要定义一个类,随着代码模块增多,类会越来越多,这叫类膨胀问题。而依赖于 value 是一种更偏向于 Ad hoc Polymorphism 的方式,Functional Programming 就属于此类,此时功能的最小单元通常使用函数。 Dispatch table 就属于一种轻量级的逻辑分派方式。 一个简单的示例: int main(){ std::map<const char… Continue Reading T230713 Dispatch table

今天谈谈 inline constexpr。 上次讲过 static constexpr,它用于 function scope/class scope ,此时 constexpr 会隐式 inline (class scope),static 则表示存储时期为 static。组合起来使用,一是能够直接在类中初始化通过 default member initializer 定义静态成员,二是所有对象能够共享数据,三是可以强保证发生于编译期,四是能够强保证 Lambdas 隐式捕获此数据。同时,也能够解决 SIOF。 上次也讲过 static inline,但是没有谈及 inline constexpr。 讨论之前,大家再回忆一个提过的准则:constexpr 在 file scope 下会隐式 internal linkage。 所以在 file scope 下不会写 static constexpr,这里的 static 是冗余的。但是单独只写一个 constexpr 修饰数据,默认的 internal linkage 会导致在多个 TUs… Continue Reading T230707 inline constexpr

提到 C++,许多人的印象绝对不会少了一个词:复杂。 的确,2000 页左右的标准,甚至一些特性单独就能成一本书,怎么能不复杂? 但也别忘了它是一门接近 40 年的编程语言。 系统大了,问题本身就很复杂,很多时候并不存在完美的解决方案。很多问题的一面是另一个系统的另一面,解决这个问题必然会带来另一个问题。所以许多时候都要分清主次,把主要模板的问题潜移默化地排到其他次要模块中去,解决当下的问题。 问题一直都没有消失,只是在这个大系统中滚来滚去。要想真正解决问题,就得不断寻求增量,增加新特性,很多旧的问题就这样通过增量解决了。随之而来的又是新的问题,循环往复~ 新特性使用起来更方便,很多时候就是因为从更高维度解决了问题。一些新语言也只是暂时把一些问题转移到了次要方面,时间一长照样暴露无遗。也正是因为总是追求完美,而问题本身又极具复杂性,且缺少许多前置组件,又要兼容旧的,所以标准才加得超慢。 不同定制实现就是在矛盾中自己决定主次,想一招吃遍天下不可能。默认定制适用范围越广,特性用起来就越简单。但一旦换个场景,增加定制是否也这么简单,就不好说了。 Bjarne 在 Herb 的某次演讲最后说: there’s a huge chunk of the C++ community that love complexity. They want every little detail of the implementation being obvious, so that they know exactly how many nanoseconds it might cost. Sometimes I’m on… Continue Reading T230705 Complex C++