如何区分pr-value与x-value并限制my_type构造函数参数类型
问题描述
给定以下C++代码:
#include <utility> struct literal_type { // ... }; class my_type { public: my_type(literal_type const& literal); // (1) my_type(literal_type && literal); // (2) // ... }; void foo() { literal_type literal_var { /* ... */ }; my_type var1 (literal_var); // 调用(1) my_type var2 (std::move(literal_var)); // 调用(2) my_type var3 (literal_type{}); // 调用(2) }
已知var1构造时传入的是左值(l-value),var2的参数是亡值(x-value),var3的参数是纯右值(pr-value)。现在希望my_type的构造函数仅接受var3这类纯右值参数,让var1和var2触发编译错误。移除构造函数(1)可以解决var1的问题,但无法区分var2的亡值和var3的纯右值,请问是否有方法区分二者?
解决方案
要实现只让纯右值(pr-value)构造my_type,同时拒绝左值和亡值(x-value),可以用下面两种方法:
方法1:C++20用requires约束直接限制
直接给移动构造函数加个编译期约束,只有参数是纯右值的时候才允许调用:
#include <utility> #include <type_traits> struct literal_type { // ... }; class my_type { public: // 先把拷贝构造删了,彻底禁止左值传入 my_type(const literal_type&) = delete; // 只接受纯右值的构造函数 my_type(literal_type&& literal) requires std::is_prvalue_v<decltype(literal)> { // 构造逻辑 } }; void foo() { literal_type literal_var {}; my_type var1(literal_var); // 编译报错:拷贝构造已删除 my_type var2(std::move(literal_var)); // 编译报错:不满足requires条件 my_type var3(literal_type{}); // 正常编译:参数是纯右值 }
方法2:用SFINAE兼容C++11及以上版本
如果要兼容旧标准,可以用模板构造函数配合类型萃取来筛选:
#include <utility> #include <type_traits> struct literal_type { // ... }; class my_type { public: // 删除拷贝构造,封死左值路径 my_type(const literal_type&) = delete; // 把普通移动构造也删了,避免匹配x-value my_type(literal_type&&) = delete; // 模板构造函数,只匹配pr-value的情况 template<typename T, typename = std::enable_if_t< std::is_same_v<std::decay_t<T>, literal_type> && std::is_prvalue_v<decltype(std::declval<T>())> >> my_type(T&& literal) { // 这里处理构造逻辑就行 } }; void foo() { literal_type literal_var {}; my_type var1(literal_var); // 编译报错:拷贝构造已删 my_type var2(std::move(literal_var)); // 编译报错:匹配到被删除的移动构造 my_type var3(literal_type{}); // 正常编译:匹配模板构造 }
原理说明
纯右值是临时对象或者字面量这种没有实体标识的表达式,而亡值是被std::move转换后、原本有实体的对象。std::is_prvalue_v能在编译期判断表达式的类别,刚好可以精准区分这两种右值,从而只放行纯右值的构造请求。
内容的提问来源于stack exchange,提问作者Arjonais
相关产品推荐
相关产品推荐

