C++ concept的requires参数的CV限定符与引用是否存在差异?
核心结论
- requires 表达式的参数的 CV 限定符、引用修饰符确实会产生差异,
requires(T& t)和requires(T t)并不始终等价。 - 你测试的
Deserializable场景下四个版本等价,是因为你的deserialize函数接受const std::string&形参,四个版本的参数都是可绑定到该形参的左值,刚好覆盖了所有合法情况,看不出差异。
1. CV 限定符的差异
requires 块内的参数会完全保留你声明的 CV 属性,如果你要检测的操作需要修改对象(比如调用非 const 成员函数、自增等),const 限定的参数会直接导致检测失败:
struct S { void modify() {} // 非const成员函数 void read() const {} }; // 允许修改参数的concept template<typename T> concept Modifiable = requires(T t) { t.modify(); }; // 不允许修改参数的concept template<typename T> concept ConstAccessible = requires(const T& t) { t.read(); }; static_assert(Modifiable<S>); // 成立 static_assert(!Modifiable<const S>); // const S不能调用非const方法,不成立 static_assert(ConstAccessible<S>); // 成立,const T&可以绑定到非const左值
2. 值和引用的差异
最典型的差异出现在参数是数组类型的场景:普通函数(包括 requires 参数)的数组类型值参数会自动退化为指针,而引用参数不会退化,这会直接影响表达式检测的结果:
// 检测参数是否为长度为5的int数组 template<typename T> concept Array5ByValue = requires(T t) { sizeof(t) == 5 * sizeof(int); }; template<typename T> concept Array5ByRef = requires(T& t) { sizeof(t) == 5 * sizeof(int); }; static_assert(!Array5ByValue<int[5]>); // 数组退化为指针,sizeof(t)等于指针大小,不成立 static_assert(Array5ByRef<int[5]>); // 数组引用不退化,sizeof(t)等于20(假设int占4字节),成立
这也是标准库 std::ranges::range 概念使用 T& 作为 requires 参数的核心原因:如果用值参数,数组类型会退化为指针,无法被识别为合法范围,而引用参数可以保留数组的原始类型,让数组正确匹配 range 要求。
3. 回到你的 Deserializable 场景
你提供的四个版本在当前业务场景下完全等价,原因是:
- 所有版本的参数
s都是左值,都可以绑定到deserialize的const std::string&形参 deserialize本身不修改输入参数,也不需要感知输入参数的所有权,因此 CV 和引用修饰不会影响调用合法性
如果你后续修改 deserialize 的签名为接受右值引用(比如 deserialize(std::string&& src)),那么四个版本的检测结果会立刻出现差异。
内容的提问来源于stack exchange,提问作者Zizheng Tai
相关产品推荐
相关产品推荐

