15 | constexpr:一个常态的世界
15 | constexpr:一个常态的世界
讲述:吴咏炜
时长16:50大小15.38M
初识 constexpr
constexpr 和编译期计算
constexpr 和 const
内联变量
constexpr 变量模板
constexpr 变量仍是 const
constexpr 构造函数和字面类型
if constexpr
output_container.h 解读
内容小结
课后思考
参考资料
赞 1
提建议
精选留言(21)
- 李亮亮2019-12-30我觉得我学习这个专栏只是为了能看懂这些新特性,写是写不出来,规则太多太复杂了。
作者回复: 很少需要自己写的。就像学了微积分大部分人也没机会用一样😂。但一点都看不懂也是会很有问题的。
11 - czh2020-02-04老师好,有个小疑问,文中提到: “上一讲的结尾,我们给出了一个在类型参数 C 没有 reserve 成员函数时不能编译的代码:” 这里提到使用 if constexpr,可以解决上述问题。这里没有过多的解释,我理解是:使用if constexpr之后,如果没有reserve成员,那就会在编译期跳过这个if中的内容,因此不会检查container.reserve()。 不知道理解是否正确?展开
作者回复: 对。一般的 if 是运行期条件语句;if constexpr 是编译期条件语句。
7 - Jerry Tan2019-12-30您好老师, 请问想学C++ 您有什么比较好的推荐的开发工具吗 谢谢
作者回复: 第 21 讲会讨论一点工具。不过你指的是什么工具呢?……如果你用 Windows,就从 Visual Studio 的免费 Community 版开始吧。
共 5 条评论2 - Slience-0°C2022-02-18常量区分编译期?和运行期?
作者回复: 对,这是很重要的一个概念。能编译期确定的东西才能做编译期计算。
1 - Slience-0°C2022-02-18老师好,现代C++如何优雅的定义字符串常量?直接使用const std::string var = "xxxx "有些静态代码检查工具会提示可能会抛出无法捕获的异常!
作者回复: 像你这种用法可以考虑用 std::string_view。 可参考我这个知乎回答: https://www.zhihu.com/question/483774144/answer/2251359893
1 - 心态正常2021-04-26吴老师,您好,有个问题想请教一下,文章开头的两个示例我在centos8上使用g++ 8.3.1的编译器编译通过了,因为没有用到constexpr的特性,预期在int a[n]这一行会报错,但是实际上并没有给出错误,这是编译器做了优化处理吗?
作者回复: 是可以过的。只有第三个例子,模板参数,才会要求特别严。 不是考大家知道哪些行哪些不行。正如下面写的:“这些问题的答案不直观。”
1 - geek2021-03-08试着回答一下两个思考问题: 1 我认为不用consexpr,就要用enable_if,类似于上一节的append方法那样,在有两种可能情况时,要写两个方法,做标签分发。这种方式的一个推广就是:有多少种可能,就要写多少个对应的分发方法。 2 不用constexpr的缺点,就是代码冗余而且不易读。那么用constexpr的优点就是代码无冗余,易读。展开
作者回复: 嗯。再补充一个,如果你有n个不同的静态条件,你需要分发的函数数量是 2^n。🤓
共 2 条评论1 - 清水2021-01-14吴老师,你好,请教个问题 constexpr int factorial(int n) { if (n == 0) { return 1; } else { return n * factorial(n - 1); } } 如果constexpr 修饰函数 这样是编译不过的,提示not a return-statement (至少c++11不行) 没有尝试过是否其他编译器 多行编译 通过 如果修改为一行表达式是没问题的,那么这是constexpr关键字 用法要求还是其他原因导致?展开
作者回复: 编译器太老,升级到至少支持C++14的编译器,比如GCC 7。 GCC 6也许也可以,但我没有环境。毕竟,现在GCC已经发布10.2了。这门课程期望你用GCC 7以上的编译器。
1 - talor2020-08-30您好, constexpr int a = 42; constexpr const int& b = a; 这个例子编译不过,编译器是gcc 10.2.1
作者回复: 我大概知道你的问题了。这两句是不能放在函数体里的。constexpr 引用只能绑定到全局变量上。
1 - Chillstep2022-07-23老师您好,这一段我发现有一些疑问:"这是因为 ODR-use 的类静态常量也需要有一个定义,在没有内联变量之前需要在某一个源代码文件(非头文件)中这样写:const int magic::number = 42; "这一段我实验了下发现并不能过编译,我认为number已经是const的,应该是没办法在赋值的了,这里应该只需要const int magic::number;即可,这样是可以过编译的。
作者回复: 👌谢谢抓虫。我回头修正一下。
- ano2021-10-18老师,我想请教一下。一个 function 标记为 inline, 但如果 function body 中有循环的话,编译器就不会在这个函数的调用处 inline? 编译器是出于什么考虑,不做这个 inline?
作者回复: 没这么简单。有循环也有可能 inline 的。各个编译器的策略并不相同,优化级别也会产生影响。 总体上编译器会对 inline 的效果有自己的评估方式,“值得”inline 才会 inline。
- 常振华2021-10-13// Type trait to detect std::pair template <typename T> struct is_pair : std::false_type {}; template <typename T, typename U> struct is_pair<std::pair<T, U>> : std::true_type {}; template <typename T> inline constexpr bool is_pair_v = is_pair<T>::value; is_pair模板并没有定义value成员啊,为什么可以::value?展开
作者回复: 它继承自 true_type 或 false_type,那里面有。
- yuchen2020-08-17template <typename T> struct is_pair : std::false_type {}; template <typename T, typename U> struct is_pair<std::pair<T, U>> : std::true_type {}; template <typename T> inline constexpr bool is_pair_v = is_pair<T>::value; 吴老师好,请问这里template <typename T, typename U> struct is_pair<std::pair<T, U>>是特化还是偏特化呢?展开
作者回复: 特化如果没有把所有的模板参数全部消掉,那就是偏特化了。
共 2 条评论 - O2020-07-16老师好,我想问一下,想容器嵌套类似vector<vector<>>之类的,output如何判断
作者回复: 就是标准的递归而已,先处理外层,看到内存是vector,继续调用,直到全部输出或遇到无法输出的内容(那就会编译报错)。
- g_boshu2020-04-01吴老师您好,我对以下代码有点儿疑惑: // Type trait to detect std::pair template <typename T> struct is_pair : std::false_type {}; template <typename T, typename U> struct is_pair<std::pair<T, U>> : std::true_type {}; template <typename T> inline constexpr bool is_pair_v = is_pair<T>::value; template <typename T, typename U> struct is_pair<std::pair<T, U>>: std::true_type {}; 看着应该是一个偏特化,模板的参数却变多了,一般偏特化不应该是参数变少吗?谢谢展开
作者回复: 是偏特化。没有规定说偏特化一定参数更少啊。
- zKerry2020-02-28int sqr(int n) { return n * n; } int main() { const int n = sqr(3); int a[n)]; } 其中:int a[n)] 没有问题?展开
作者回复: 谢谢指出问题。这是修改中引入的问题。回头就修掉。
- 光城~兴2020-01-08对老师的输出函数进行修改:如下 template<typename T, typename Cont> auto output_element(std::ostream &os, const T &element, const Cont &) -> typename std::enable_if<is_pair<typename Cont::value_type>::value, bool>::type { os << element.first << " => " << element.second; return true; } template<typename T, typename Cont> auto output_element(std::ostream &os, const T &element, const Cont &) -> typename std::enable_if<!is_pair<typename Cont::value_type>::value, bool>::type { os << element; return false; } 调用处: output_element(os, elem, container); 这个方法学习自老师之前讲过的SFINAE! 另外,针对老师的代码有些疑问: 老师代码调用处: output_element(os, *it, container, is_pair<element_type>()); 实际上在这里就确定了element_type是不是pair,也就是这里传递进去直接就是true_type或者false_type,针对,map/vector/set等直接就可以区分开来,不需要写:std::declval<typename Cont::key_type>()。 也是可以正常完成输出的,但是当传递的是true_type且容器没有key_type的时候就是SFINAE问题,调用另一个重载函数。 问题是,一个容器元素是pair,那么is_pair<element_type>()就是true_type,而既然是pair了,也就有了key_type,所以这个必然成立,也就是写与不写都可以。另外,当不是pair,就是false_type,肯定调false_type的output_element重载咯,所以我得出这里写这个std::declval<typename Cont::key_type>()没有啥子用,并且代码测试过确实可以不写,望老师指点!展开
作者回复: 我并不希望把vector<pair<…>> 输出成“=>”的形式。我要仅在输出map、unordered_map的元素时才使用“=>”。
共 3 条评论 - 光城~兴2020-01-07constexpr变量仍是const这一块的例子: ```cpp constexpr int a = 42; constexpr const int& b = a; ``` 第二行会报错 需要一个常量表达式 去掉constexpr是不是更好? 貌似对这一块解释没影响~展开
作者回复: 关键是你怎么用 b。如果你需要它是编译期常量,它就应该是 constexpr。
- 花晨少年2020-01-04为什么文章开头提到的两个例子,都是合法的吗,编译运行都没问题。 第一个例子理所当然的像应该有问题,但是仔细想了下,为什么要有问题呢,数据大小为什么就要编译器确定呢,运行期确定不行吗。而结果是确实没有问题,这里面的玄机是什么呢 第二个例子应该是 int a[n];吧 第二个例子是因为const常量的原因,编译器会强制sqr函数编译器运行特定参数吗展开
作者回复: 对,纠正得对。我回头改一下。 能不能编译本身不是关键。关键是答案不明显,依赖于一些细微的 C++ 规则。
共 2 条评论 - tt2019-12-30文中一开始用constexpr 改造的例子,之所以可以,一定是在使用constexpr的地方“就地”调用了赋值运算符右侧的函数,这样才能得到一个编译期的常量,所以,“内联”是constexpr的应有之意。但是在类外,必须加上inline才可以。 const本质是一个运行时常量,constexpr才是编译期常数,除了内联展开这个含义,再根据文中ODR的表述,说明constexpr变量是切实分配了内存空间的,是一个左值对象。综合上面的考虑,constexpr意味着被声明的对象是存放在数据段里面的。 constexpr 变量模板表达一个和某个类型相关的编译期常量,让变量也可以是模板,这句话在本课中,我觉得理解成“把模板对象用一个变量命名”更合适,即把所有符号都绑定到了一个实体上,这样if constexpr才变得可行。解决了上一讲中说的c++中不能像Python一样写代码的问题。展开
作者回复: 作为编译期常数,没理由认为一个 constexpr 变量一定会在数据段里的,尤其是优化编译的情况。一般而言,只有非优化编译、又有 ODR-use 才能在 data 段里找到其定义。