C++11下修改std::get运行时索引代码触发越界错误求助
修复C++11中运行时获取tuple元素的索引越界问题
我修改了一段原本支持向std::tuple的std::get<>传入运行时整数的代码,将其从打印tuple索引值改为返回对应值后,触发了index out of range错误。编译器仅支持C++11,修改后的问题代码如下:
#include <iostream> #include <string> #include <tuple> #include <type_traits> template<typename Tuple> std::tuple_element<std::tuple_size<Tuple>::value, Tuple> get_impl(size_t i, Tuple const& t, typename std::tuple_size<Tuple>::type) {} template<size_t N, typename Tuple, typename = std::enable_if<std::tuple_size<Tuple>::value != N>> std::tuple_element<N, Tuple> get_impl(size_t i, Tuple const& t, std::integral_constant<size_t, N>) { if(i == N) { return std::get<N>(t); } else { return get_impl(i, t, std::integral_constant<size_t, N+1>()); } } template<typename Tuple> std::tuple_element<std::tuple_size<Tuple>::value, Tuple> get(size_t i, Tuple const& t) { return get_impl(i, t, std::integral_constant<size_t, 0>()); } int main() { std::tuple<int, double, std::string> t(7, 9.8, "text"); int idx = 0; std::cout << get(idx, t) << std::endl; }
问题根源
- 非法返回类型:主
get函数和终止版get_impl使用std::tuple_size<Tuple>::value作为索引获取std::tuple_element,但tuple的合法索引范围是0到std::tuple_size<Tuple>::value - 1,这个索引对应的元素类型不存在,直接触发编译期的"index out of range"错误。 - 递归逻辑缺陷:当传入合法索引时,递归返回的元素类型与主函数声明的返回类型不匹配;索引越界时也没有正确的终止处理逻辑。
兼容C++11的修复方案
由于C11没有原生的变体类型(如C17的std::variant),提供两种可行的修复方式:
方式1:使用Boost.Any存储任意类型(需Boost库)
这是最通用的方案,能保留元素的原始类型:
#include <iostream> #include <string> #include <tuple> #include <type_traits> #include <stdexcept> #include <boost/any.hpp> // 递归终止:索引越界时抛出异常 template<typename Tuple> boost::any get_impl(size_t i, const Tuple& t, std::integral_constant<size_t, std::tuple_size<Tuple>::value>) { throw std::out_of_range("Tuple index out of range"); } // 递归匹配索引 template<size_t N, typename Tuple, typename = typename std::enable_if<N < std::tuple_size<Tuple>::value>::type> boost::any get_impl(size_t i, const Tuple& t, std::integral_constant<size_t, N>) { if(i == N) { return boost::any(std::get<N>(t)); } else { return get_impl(i, t, std::integral_constant<size_t, N+1>()); } } // 对外接口 template<typename Tuple> boost::any get(size_t i, const Tuple& t) { return get_impl(i, t, std::integral_constant<size_t, 0>()); } int main() { std::tuple<int, double, std::string> t(7, 9.8, "text"); int idx = 0; try { boost::any val = get(idx, t); // 根据实际类型提取值 if(int* p = boost::any_cast<int>(&val)) std::cout << *p << std::endl; else if(double* p = boost::any_cast<double>(&val)) std::cout << *p << std::endl; else if(std::string* p = boost::any_cast<std::string>(&val)) std::cout << *p << std::endl; } catch(const std::exception& e) { std::cerr << e.what() << std::endl; } }
方式2:转换为字符串返回(无需额外库)
如果仅需要获取可输出的值,可直接将元素转换为std::string:
#include <iostream> #include <string> #include <tuple> #include <type_traits> #include <stdexcept> #include <sstream> // 递归终止:索引越界时抛出异常 template<typename Tuple> std::string get_impl(size_t i, const Tuple& t, std::integral_constant<size_t, std::tuple_size<Tuple>::value>) { throw std::out_of_range("Tuple index out of range"); } // 递归匹配索引并转换为字符串 template<size_t N, typename Tuple, typename = typename std::enable_if<N < std::tuple_size<Tuple>::value>::type> std::string get_impl(size_t i, const Tuple& t, std::integral_constant<size_t, N>) { if(i == N) { std::ostringstream oss; oss << std::get<N>(t); return oss.str(); } else { return get_impl(i, t, std::integral_constant<size_t, N+1>()); } } // 对外接口 template<typename Tuple> std::string get(size_t i, const Tuple& t) { return get_impl(i, t, std::integral_constant<size_t, 0>()); } int main() { std::tuple<int, double, std::string> t(7, 9.8, "text"); int idx = 0; try { std::cout << get(idx, t) << std::endl; } catch(const std::exception& e) { std::cerr << e.what() << std::endl; } }
核心修改说明
- 修正终止版
get_impl的返回类型,改为合法的容器类型,并在索引越界时抛出标准异常。 - 调整
std::enable_if的条件为N < std::tuple_size<Tuple>::value,确保递归仅在合法索引范围内执行。 - 主
get函数返回统一类型,解决C++11中无法根据运行时值确定返回类型的限制。
内容的提问来源于stack exchange,提问作者Harry
相关产品推荐
相关产品推荐

