You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在解引用指针后恢复原对象值类别以调用正确函数重载?

问题:解引用指针后恢复原对象值类别以调用正确重载

我想调用重载的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 08:42:24