如何将C++万能引用限制为const T&或T&&类型?
能否让万能引用仅接受const左值引用或右值引用,拒绝普通左值引用?
可以通过模板约束实现这个需求,先看原问题场景:
原问题代码示例
#include <string> #include <iostream> struct S { S() {} S(const S&) { std::cout << "copied" << std::endl; } S(S&&) { std::cout << "moved" << std::endl; } }; auto foo(auto&& arg) { return std::move(arg); } int main() { std::cout << "temp call" << std::endl; foo(S()); std::cout << std::endl << "variable call" << std::endl; S s; foo(s); std::cout << std::endl << "move call" << std::endl; foo(std::move(s)); }
原输出结果
temp call moved variable call moved <--- 此处期望触发拷贝构造(copied) move call moved
用户已知正确处理万能引用需使用std::forward:
auto foo(auto&& arg) { return std::forward<decltype(arg)>(arg); }
但希望直接在函数签名层面约束参数只能是const T&或T&&,拒绝普通左值引用T&。
解决方案
方法1:C++20 requires表达式
利用C++20的约束表达式直接在签名中限制参数类型:
#include <type_traits> #include <string> #include <iostream> struct S { S() {} S(const S&) { std::cout << "copied" << std::endl; } S(S&&) { std::cout << "moved" << std::endl; } }; auto foo(auto&& arg) requires (std::is_const_v<std::remove_reference_t<decltype(arg)>> && std::is_lvalue_reference_v<decltype(arg)>) || std::is_rvalue_reference_v<decltype(arg)> { return std::forward<decltype(arg)>(arg); } int main() { std::cout << "temp call" << std::endl; foo(S()); // 正常调用,匹配右值引用,输出moved std::cout << std::endl << "const变量调用" << std::endl; const S cs; foo(cs); // 正常调用,匹配const左值引用,输出copied std::cout << std::endl << "move调用" << std::endl; S s; foo(std::move(s)); // 正常调用,匹配右值引用,输出moved // S s2; // foo(s2); // 编译错误:不满足约束,普通左值引用被拒绝 }
方法2:SFINAE(C++11及以上兼容)
针对C++20之前的版本,使用std::enable_if实现约束:
#include <type_traits> #include <string> #include <iostream> struct S { S() {} S(const S&) { std::cout << "copied" << std::endl; } S(S&&) { std::cout << "moved" << std::endl; } }; template<typename T, typename = std::enable_if_t< (std::is_const_v<std::remove_reference_t<T>> && std::is_lvalue_reference_v<T>) || std::is_rvalue_reference_v<T> >> auto foo(T&& arg) { return std::forward<T>(arg); } int main() { std::cout << "temp call" << std::endl; foo(S()); // 正常调用 std::cout << std::endl << "const变量调用" << std::endl; const S cs; foo(cs); // 正常调用 std::cout << std::endl << "move调用" << std::endl; S s; foo(std::move(s)); // 正常调用 // S s2; // foo(s2); // 编译错误:模板推导失败 }
约束原理
- 对于
const T&:std::remove_reference_t<T>是const T(is_const_v为true),且是左值引用(is_lvalue_reference_v为true),满足第一个条件。 - 对于
T&&:is_rvalue_reference_v<T>为true,满足第二个条件。 - 对于普通左值引用
T&:两个条件都不满足,编译时直接报错,拒绝此类调用。
内容的提问来源于stack exchange,提问作者user3612643
相关产品推荐
相关产品推荐

