带右值引用的可变参数模板运算符>>重载技术问询
如何为模板类型实现接收右值引用的>>运算符?
首先还原你的场景和问题:
我原本有这样的代码定义:
template<typename ... TL> struct TemplatedType { }; template<typename ... TL> std::istream& operator>>(std::istream& is, TemplatedType<TL ...> & sp) { // do stuff return is; }使用示例:
std::istringstream iss("I'd like some pancakes, please"); TemplatedType<int> a; iss >> a;这段代码能正常运行,但我希望把模板参数以右值引用的形式接收,也就是定义类似
template<typename ... TL> std::istream& operator...的运算符,请问该怎么实现?
其实你要的就是为TemplatedType实现接收右值引用参数的operator>>重载,直接修改参数类型就能搞定,下面分两种情况给你说明:
1. 单独定义右值引用版本的运算符
直接把原来的左值引用参数改成右值引用,模板参数包的写法保持不变即可:
template<typename ... TL> std::istream& operator>>(std::istream& is, TemplatedType<TL ...> && sp) { // 这里可以针对右值对象做特殊处理,比如移动内部资源 // 也可以复用原有逻辑,比如: // is >> /* 操作sp的成员变量 */; return is; }
这样之后,编译器会根据你传入的对象值类别自动匹配对应版本:
// 传入左值,调用原左值引用版本 TemplatedType<int> a; iss >> a; // 传入右值,调用新的右值引用版本 iss >> TemplatedType<int>{};
2. 用转发引用兼容左值和右值
如果不想写两个重载,也可以用转发引用(Forwarding Reference)写一个通用版本,同时兼容左值和右值。不过需要加个限制,避免这个运算符被其他无关类型误匹配:
#include <type_traits> // C++20及以上版本的写法 template<typename T> std::enable_if_t<std::is_specialization_v<T, TemplatedType>, std::istream&> operator>>(std::istream& is, T&& sp) { // 使用std::forward保持原始值类别,左值还是左值,右值还是右值 // do stuff with std::forward<T>(sp) return is; }
如果是C++17及更早版本,std::is_specialization_v还不存在,你可以自己实现一个判断模板特化的 trait:
template<typename T, template<typename...> class Template> struct is_specialization : std::false_type {}; template<template<typename...> class Template, typename... Args> struct is_specialization<Template<Args...>, Template> : std::true_type {}; template<typename T, template<typename...> class Template> constexpr bool is_specialization_v = is_specialization<T, Template>::value; // 用自定义trait实现通用运算符 template<typename T> std::enable_if_t<is_specialization_v<T, TemplatedType>, std::istream&> operator>>(std::istream& is, T&& sp) { // 同样用std::forward处理值类别 return is; }
这样不管你传左值还是右值,这个运算符都能正确匹配,而且不会干扰其他类型的operator>>逻辑。
内容的提问来源于stack exchange,提问作者Draex_
相关产品推荐
相关产品推荐

