What are the ranking rules for reference bindings? Let’s consider the following example: void f(int);         // #1 void f(int&);        // #2 void f(int const&);  // #3 void f(int&&);       // #4 void f(int const&&); // #5 Here are some key points: #1 is a function that accepts borrowed int arguments. #2 is a function that takes an lvalue reference. #3 is a function that takes… Continue Reading Normal OR Rules for Reference Bindings

本篇可以结合 Left-to-Right vs. Right-to-Left Coding Styles 阅读,属于同一主题。本篇侧重于讲解具体的类型推导规则。 Decltype Specifier 在静态类型语言中,一个变量需要由类型说明符指定,而随着 C++ 的发展,类型也可以从表达式推导出来,不必显式写出。 一切始于 C++11 decltype(E),Decltype 也属于说明符,接受一个表达式参数。也可以传入一个变量,因为变量名属于 id-expressions,也是表达式。 这里的核心在于,表达式其实包含三部分信息:type, value, 和 value category。使用 Decltype 推导出的表达式结果与原类型的信息并不总是相同,这种不一样的依据就是类型推导规则。 Decltype 的推导规则需要分为两种情况讨论,E 和 (E),也就是说,多加一个括号将改变推导规则。 先来看第一种,E 的情况。 如果 E 是 id-expressions 或者类成员名称访问,此时推导结果的 type 和 value 都和 E 所对应的实体相同,但是不会保留原有的 value category。 int a = 42; static_assert(std::is_same_v<decltype(a), int>); std::println("lvalue:… Continue Reading Mastering Placeholder Type Deduction

进入 Modern C++,声明风格由 Right-to-Left 逐渐转变为 Left-to-Right,个中差异,优劣得失,且看本篇内容。 前言 Classic C++ 中,声明风格是自右向左,如: int f() {} int a = 42; std::string s{"str"}; 而 Modern C++ 中变为自左向右,对应写法为: auto f() -> int {} auto a = 42; auto s = std::string{"str"}; 其实很多编程语言都采用或支持 Left-to-Right 这种声明风格,下面列举几种。 Rust: fn f(num: i32) -> i32 {} let x = f(5);… Continue Reading Left-to-Right vs. Right-to-Left Coding Styles

再再再补充一个重载决议的例子。 大家可能在某些地方见过 ::std:: 这样的代码,比如 ::std::swap,::std::vector,::std::nullptr_t。 在 Qualified Name Lookup 一节的子节 Namespace Member Lookup 已经介绍,名称前面以 :: 修饰表示在全局作用域下查找。 一个例子: namespace A { namespace B { void f() { std::cout << "A::B::f()\n"; } } } namespace B { void f() { std::cout << "B::f()\n"; } } namespace A { void h() { ::B::f();… Continue Reading std:: versus ::std::

今天这篇讲 Monads,对 C++ devs 来说是一个比较新的概念,也是一个比较难理解的概念。本文将概念和实践串起来讲,先讲抽象理论,后面讲具体实现和使用,以全面理解其定位。 Language 编程语言用于操作机器以辅助人类解决问题,主体是人和机器,目的是解决问题。人通过机器解决问题,编程语言是人和机器之间沟通的桥梁。 问题解决最基本的流程为定义问题、分析问题、寻找策略及选择策略。简单来说,你有一些东西,想通过一些行为,再得到一些东西。问题就是理想和现实之间的差距。 这其中最基本的构成要素就是东西和行为,也就是编程语言当中的数据和函数。一个大问题不可能通过一个行为就能解决,往往需要拆分为一个个小问题,再逐个击破,故根据问题复杂程度,数据和函数的数量也会随之变化。 主体虽分为人和机器,但却是由机器来完成具体工作。最初的语言,靠机器更近,尽管效率高,人和机器沟通起来却是多有不便;随后的语言,便靠人更近,使人和机器的沟通方式,更加接近人与人之间的沟通,这才简化了编程难度。 常言道,月满则亏,水满则溢,凡事过犹不及。太靠近机器的语言,尽管效率高,但却难以理解;太靠近人的语言,虽说易理解,效率却颇低。因此,只有在明确仅注重效率或简易的情况下,才会使用这类语言,许多语言其实都处于中间,保持效率的同时也避免太过复杂。 Imperative versus Declarative 由机器到人,编程语言是把机器所能识别的具体指令,抽象为人所能理解的词句。 声明式语言相比命令式语言,离人更近,命令式更关注具体,声明式更关注抽象。抽象是把不变的都封装起来,从而避免去写一个个重复的指令,比如原本想要修改 std::vector 的元素,你需要使用 if、for 等等指令,这些再往上抽象一层就是过滤和变换两个步骤,直接使用 v | filter | transform 则更加言简意赅。因此,声明式用起来要更加简洁,使人更加专注于问题解决步骤。 具体中包含了变与不变,抽象中提取了不变的部分。具体就像是白话文,抽象就像是文言文,前者通俗易懂,后者简洁精炼。命令式让人更加专注于行为的细枝末节,事无具细,一切尽在掌握之中;声明式让人更加关注于行为步骤,抽离细节,一切环环相扣。 也可以说命令式注重问题解决过程,而声明式注重问题解决结果。不关心过程,只想要结果,展现在语言上就是只调用接口,不关心实现。因此,从视角上来看,命令式更偏向于微观,声明式更偏向于宏观。宏观适合思考问题,微观适合解决问题,前者像是将军,只谋战略,后者则像是小兵,具体实施。 将无兵而不行,兵无将而不动。抽象与具体当中,虽然真正起作用的是具体的指令,但使用的却是抽象的接口。接口使来结构清晰,能更加专注于问题逻辑,跳出问题细节。抽象与具体紧密连接,只要抽象,抽象从何来?只要具体,具体何以用? 是以范式不同,本质是解决问题思维的不同,是侧重点的不同。 What is Monads C++ 有许多范式,面向过程、面向对象、泛型编程、函数式编程…… 许多人发现 Modern C++ 写来越发简单,这种简单写法就谓之 Modern,但这种简洁从何而来?本质就是范式的转变。 C++11 至今,C++ 已逐步从函数式编程中汲取了许多特性,Monads 就是其中非常重要的一个特性。C++23 中也加入不少 Monads,简化了一些原本较为繁琐的操作。 那么,什么是 Monads? 这个概念来自范畴论,称为单子,牵扯概念颇多,且不易理解,因此我们结合 C++… Continue Reading Monads in Modern C++, What, Why, and How