如何在解引用指针后恢复原对象值类别以调用正确函数重载?
问题:解引用指针后恢复原对象值类别以调用正确重载
我想调用重载的hello函数,它分别接受Json的左值引用和右值引用参数。Json对象由JsonKey通过重载的operator Json()转换生成。但现在的问题是:当我解引用Logger类中存储的指针获取JsonKey引用时,原对象的左值/右值类别信息丢失了——解引用操作总是得到左值引用(比如std::forward<decltype(*_curr_key)> (*_curr_key)始终返回左值引用)。请问如何在解引用指针后恢复原对象的值类别(或者预先存储该类别以便后续获取),从而调用hello的正确重载?
原代码
#include <iostream> #include <sstream> #include <string> struct Json { Json() = default; }; auto hello(const Json& myjson) { std::cout << "Copy called" << std::endl; } auto hello(Json&& myjson) { std::cout << "Move called" << std::endl; } struct JsonKey { JsonKey(std::string key) : _key(std::move(key)) {} template<typename T> auto operator<<(T&& token) -> JsonKey& { _value << std::forward<T>(token); return *this; } operator Json() && { std::cout << "move conversion called" << std::endl; auto json = Json{}; return json; } operator Json() const& { std::cout << "copy conversion called" << std::endl; auto json = Json{}; return json; } std::ostringstream _value; std::string _key; }; struct Logger { ~Logger() { if (_curr_key) { hello(*_curr_key); // <-- 此处需调用正确的移动或拷贝重载 } } template<typename T> auto operator<<(T&& token) -> Logger& { using T_ = std::remove_reference_t<std::remove_cv_t<T>>; if constexpr(std::is_same_v<T_, JsonKey>) { if (_curr_key) { hello(*_curr_key); // <-- 此处同样需调用正确重载 } _curr_key = &token; } else { if (_curr_key) { (*_curr_key) << std::forward<T>(token); } } return *this; } JsonKey* _curr_key = nullptr; }; int main() { auto mykey = JsonKey("1stKey"); // 这是一个左值 mykey << "First value is " << 20; Logger() << mykey << JsonKey("2ndKey") << "Second value is " << 10; // 此处的JsonKey对象是右值引用 }
解决方案:存储值类别信息并转发
核心思路是在Logger中额外记录原对象的值类别(左值/右值),后续根据这个标记决定如何转发引用,触发对应的转换运算符和hello重载。
修改后的Logger代码
struct Logger { ~Logger() { if (_curr_key) { if (_is_rvalue) { hello(std::move(*_curr_key)); // 转发为右值,触发移动转换 } else { hello(*_curr_key); // 传递左值,触发拷贝转换 } } } template<typename T> auto operator<<(T&& token) -> Logger& { using T_ = std::remove_reference_t<std::remove_cv_t<T>>; if constexpr(std::is_same_v<T_, JsonKey>) { if (_curr_key) { if (_is_rvalue) { hello(std::move(*_curr_key)); } else { hello(*_curr_key); } } _curr_key = &token; // 根据传入的引用类型标记值类别 _is_rvalue = std::is_rvalue_reference_v<T&&>; } else { if (_curr_key) { (*_curr_key) << std::forward<T>(token); } } return *this; } JsonKey* _curr_key = nullptr; bool _is_rvalue = false; // 新增:记录原对象是否为右值 };
原理说明
- 利用
std::is_rvalue_reference_v<T&&>判断传入的token类型:根据引用折叠规则,左值引用传入时T&&会变为左值引用,右值引用传入时保持右值引用,以此标记_is_rvalue。 - 当原对象是右值时,用
std::move(*_curr_key)将解引用得到的左值引用转换为右值引用,触发JsonKey的operator Json() &&移动转换,进而调用hello(Json&&)重载。 - 左值场景直接传递引用,触发拷贝转换和
hello(const Json&)重载。
内容的提问来源于stack exchange,提问作者glades
相关产品推荐
相关产品推荐

