代码之家  ›  专栏  ›  技术社区  ›  Gene

元组到变量的转换问题

  •  1
  • Gene  · 技术社区  · 1 年前

    我需要访问中的值 std::tuple 按运行时的索引。为此,我编写了一个函数,返回 std::variant 为指定的索引初始化了相同的类型。实现涉及递归模板实例化:

    template<std::size_t N>
    constexpr auto get_n = [](/*tuple-like*/auto&& t, std::size_t index)
    {
        using variant_type = decltype(std::apply([](auto&&... v){
            return std::variant<std::monostate, std::remove_cvref_t<decltype(v)>...>{};
        }, t));
    
        constexpr auto size = std::tuple_size_v<std::remove_cvref_t<decltype(t)>>;
    
        if constexpr(N > size)
            return variant_type{};
        else 
        {
            if(N == index + 1)
                return variant_type(std::in_place_index<N>, std::get<N - 1>(t));
            else
                return get_n<N + 1>(t, index);
        }
    };
    
    constexpr auto tuple_to_variant(/*tuple-like*/auto&& t, std::size_t index)
    {
        return get_n<1>(std::forward<decltype(t)>(t), index);
    }
    

    我使用的是c++23编译器。问题是只有gcc编译时没有错误。clang和MSVC编译器进入无限实例化循环。这是编译器中的错误吗?如何修复它在所有三个编译器中编译的代码?

    这是编译器资源管理器 demo

    1 回复  |  直到 1 年前
        1
  •  2
  •   康桓瑋    1 年前

    由于您是递归地调用lambda,所以这只能通过C++23显式对象参数实现。这应该有效:

    constexpr auto get_n = []<std::size_t N>
      (this auto self, /*tuple-like*/auto&& t, std::size_t index)
    {
        using variant_type = decltype(std::apply([](auto&&... v){
            return std::variant<std::monostate, std::remove_cvref_t<decltype(v)>...>{};
        }, t));
    
        constexpr auto size = std::tuple_size_v<std::remove_cvref_t<decltype(t)>>;
    
        if constexpr(N > size)
            return variant_type{};
        else 
        {
            if(N == index + 1)
                return variant_type(std::in_place_index<N>, std::get<N - 1>(t));
            else
                return self.template operator()<N + 1>(t, index);
        }
    };
    
    constexpr auto tuple_to_variant(/*tuple-like*/auto&& t, std::size_t index)
    {
        return get_n.operator()<1>(std::forward<decltype(t)>(t), index);
    }
    

    Demo

        2
  •  1
  •   LHLaurini    1 年前

    如果您使用

    template<std::size_t N>
    constexpr auto get_n(/*tuple-like*/auto&& t, std::size_t index)
    

    相反没有理由 get_n 为lambda。

        3
  •  0
  •   Gene    1 年前

    这是另一种可能的解决方案,被每个编译器所接受。优点是lambda函数在内部是局部的 tuple_to_variant ,所以它不会在外部范围内创建任何名称。

    inline auto tuple_to_variant(auto&& t, std::size_t index)
    {
        constexpr auto get_n = []<auto N>(this auto&& self, auto&& t, std::size_t index, std::index_sequence<N>)
        {
            using variant_type = decltype(std::apply([](auto&&... v){
                return std::variant<std::monostate, std::remove_cvref_t<decltype(v)>...>{};
            }, t));
    
            constexpr auto size = std::tuple_size_v<std::remove_cvref_t<decltype(t)>>;
    
            if constexpr(N > size)
                return variant_type{};
            else 
            {
                if(N == index + 1)
                    return variant_type(std::in_place_index<N>, std::get<N - 1>(t));
                else
                    return self(t, index, std::index_sequence<N + 1>{});
            }
        };
    
        return get_n(std::forward<decltype(t)>(t), index, std::index_sequence<1>{});
    }
    

    代码资源管理器 demo