为何使用std::is_arithmetic_v的C++20 concept(requirement expression)无法强制执行类型约束?
为何使用std::is_arithmetic_v的C++20 concept(requirement expression)无法强制执行类型约束?
我最近跟着Marius Bancila在2022年出版的《Template Metaprogramming with C++: Learn everything about C++ templates and unlock the power of template metaprogramming》这本书里的例子实现C++20概念时,踩了个坑——我写了两个看似等价的arithmetic概念定义,结果其中一个根本起不到约束类型的作用。
先给大家看看这两个版本:
版本1(无效的约束)
template<typename T> concept arithmetic = requires(T a, T b) { { std::is_arithmetic_v<T> } -> std::convertible_to<bool>; };
版本2(有效的约束)
template<typename T> concept arithmetic = std::is_arithmetic_v<T>;
最开始我以为这俩是一回事,但实际测试发现,版本1居然允许std::string这种明显不是算术类型的参数通过约束,这完全不符合预期!
后来我才搞明白问题出在哪:
- 版本2是直接把
std::is_arithmetic_v<T>这个常量表达式作为概念的核心条件,只有当它的值为true时,类型T才满足这个概念,这是正确的用法。 - 版本1的问题在于,我用了requires表达式里的简单要求(就是带大括号的那种),它只会检查
std::is_arithmetic_v<T>这个表达式本身是否合法,以及它的结果能不能转换成bool——但不管这个结果是true还是false!哪怕T是std::string,std::is_arithmetic_v<std::string>是false,但这个表达式本身是合法的(它就是个bool常量),所以整个requires表达式都会被判定为满足,概念自然就失效了。
如果非要在requires表达式里实现这种类型检查,得用嵌套要求,也就是再加一层requires关键字来包裹常量表达式,像这样:
template<typename T> concept arithmetic = requires(T a, T b) { requires std::is_arithmetic_v<T>; };
这种写法就会强制要求std::is_arithmetic_v<T>必须为true,才能满足概念,和版本2的效果是一致的。
内容来源于stack exchange
相关产品推荐
相关产品推荐

