能否使用非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
相关产品推荐
相关产品推荐

