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

能否使用非const变量与std::as_const()从tuple中获取元素?

能否向std::get<>()传入变量?

我想实现一个结构体参数初始化函数,支持通过std::set指定要替换的参数索引,用std::tuple传入自定义参数——如果索引在set中存在,就用tuple里的对应值替换默认参数。为了避免传入和参数数量一致的tuple(填充大量占位符),我想用一个滚动索引i来跟踪tuple的下一个元素位置,但直接用变量i作为std::get<>()的参数会报错,尝试用std::as_const(i++)也不行,想知道原因和解决办法。

简化代码如下:

#include <iostream>
#include <tuple>
#include <utility>
#include <set>

struct MyStruct
{
    int my_int;
    char my_char;
    std::string my_string;

    template <typename...T>
    void initParams(std::set<int> paramIndices=std::set<int>(), std::tuple<T...> customParams=std::tuple<T...>())
    {   
        auto i=0;

        // size of set and tuple must match
        if(paramIndices.size()==std::tuple_size<std::tuple<T...>>{})
        {
            // if values are present, substitute them

            // these throw an error, which is understandable; i is not const, const template argument is required for std::get<>
            my_int = paramIndices.contains(0) ? std::get<i++>(customParams) : 0;
            my_char = paramIndices.contains(1) ? std::get<i++>(customParams) : 'a';
            my_string = paramIndices.contains(2) ? std::get<i++>(customParams) : "hello";

            // attempt to use as_const() to introduce const-ness
            // why do these throw an error as well?
            my_int = paramIndices.contains(0) ? std::get<std::as_const(i++)>(customParams) : 0;
            my_char = paramIndices.contains(1) ? std::get<std::as_const(i++)>(customParams) : 'a';
            my_string = paramIndices.contains(2) ? std::get<std::as_const(i++)>(customParams) : "hello";
        }
    }
    
    /* do stuff with params
    ...
    */

};

int main()
{
    std::set<int> s{0, 2};
    std::tuple t{500, "world"};

    MyStruct().initParams(s, t);
}

编译命令:g++ -std=c++20 test.cpp

为什么std::as_const(i++)也会报错?

std::get<>()的模板参数要求是编译期常量表达式,而std::as_const(i++)只是将变量转为const引用,变量i的值是在运行时动态变化的,不属于编译期可确定的常量,因此无法作为模板参数传入。不管变量是不是const,只要它的值不能在编译阶段确定,就不能用来实例化std::get模板。

可行的解决方法

方法1:利用tuple迭代器遍历元素(C++20及以上)

C++20开始支持std::tuple的迭代器,可以直接用迭代器逐个获取元素,无需手动管理编译期索引:

#include <iostream>
#include <tuple>
#include <utility>
#include <set>
#include <iterator>

struct MyStruct
{
    int my_int;
    char my_char;
    std::string my_string;

    template <typename...T>
    void initParams(std::set<int> paramIndices=std::set<int>(), std::tuple<T...> customParams=std::tuple<T...>())
    {   
        if(paramIndices.size() != std::tuple_size_v<std::tuple<T...>>)
            return;

        auto it = std::begin(customParams);
        my_int = paramIndices.contains(0) ? *it++ : 0;
        my_char = paramIndices.contains(1) ? *it++ : 'a';
        my_string = paramIndices.contains(2) ? *it++ : "hello";
    }
};

int main()
{
    std::set<int> s{0, 2};
    std::tuple t{500, "world"};

    MyStruct ms;
    ms.initParams(s, t);
    std::cout << ms.my_int << " " << ms.my_char << " " << ms.my_string << std::endl;
}

方法2:实现运行时获取tuple元素的辅助函数

如果需要兼容C++17及更早版本,可以借助std::index_sequence生成所有编译期索引,再在运行时匹配目标索引:

#include <iostream>
#include <tuple>
#include <utility>
#include <set>
#include <stdexcept>
#include <type_traits>

template <typename Tuple, std::size_t... Idx>
auto get_runtime_impl(const Tuple& t, std::size_t idx, std::index_sequence<Idx...>)
{
    using RetType = std::common_type_t<std::tuple_element_t<Idx, Tuple>...>;
    // 遍历所有编译期索引,匹配则返回对应元素
    bool found = false;
    RetType result{};
    ((idx == Idx ? (result = static_cast<RetType>(std::get<Idx>(t)), found = true) : void()), ...);
    if (!found)
        throw std::out_of_range("Index exceeds tuple size");
    return result;
}

template <typename Tuple>
auto get_runtime(const Tuple& t, std::size_t idx)
{
    return get_runtime_impl(t, idx, std::make_index_sequence<std::tuple_size_v<Tuple>>{});
}

struct MyStruct
{
    int my_int;
    char my_char;
    std::string my_string;

    template <typename...T>
    void initParams(std::set<int> paramIndices=std::set<int>(), std::tuple<T...> customParams=std::tuple<T...>())
    {   
        if(paramIndices.size() != std::tuple_size_v<std::tuple<T...>>)
            return;

        std::size_t i = 0;
        my_int = paramIndices.contains(0) ? get_runtime(customParams, i++) : 0;
        my_char = paramIndices.contains(1) ? get_runtime(customParams, i++) : 'a';
        my_string = paramIndices.contains(2) ? get_runtime(customParams, i++) : "hello";
    }
};

int main()
{
    std::set<int> s{0, 2};
    std::tuple t{500, "world"};

    MyStruct ms;
    ms.initParams(s, t);
    std::cout << ms.my_int << " " << ms.my_char << " " << ms.my_string << std::endl;
}

内容的提问来源于stack exchange,提问作者enoon.erehwon

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 11:24:54