使用auto和std::make_tuple遍历std::tuple时出现编译错误
我学到了一种遍历std::tuple的实现,但调用traverse(_functions, _data_1)时出现编译错误,无法理解显式声明类型的std::tuple<char, int, float, std::string> _data_0和用std::make_tuple+auto的_data_1在约束条件上的差异。
代码实现
#include <concepts> #include <iostream> #include <tuple> #include <type_traits> template <std::size_t t_idx, typename... t_function, typename... t_data> requires(... and std::is_invocable_r_v< void, t_function, t_data &>) void traverse_each(std::tuple<t_function...> p_function, std::tuple<t_data...> &p_data) { std::get<t_idx>(p_function)(std::get<t_idx>(p_data)); } template <std::size_t... t_idxs, typename... t_function, typename... t_data> requires(... and std::is_invocable_r_v< void, t_function, t_data &>) void traverse_all(std::index_sequence<t_idxs...>, std::tuple<t_function...> p_function, std::tuple<t_data...> &p_data) { (traverse_each<t_idxs>(p_function, p_data), ...); } template <typename... t_function, typename... t_data> requires(... and std::is_invocable_r_v< void, t_function, t_data &>) void traverse(std::tuple<t_function...> p_function, std::tuple<t_data...> &p_data) { traverse_all(std::make_index_sequence<sizeof...(t_data)>{}, p_function, p_data); } int main() { auto _functions{std::make_tuple( [](char &p_char) { std::cout << "char: " << p_char << std::endl; }, [](int &p_int) { std::cout << "int: " << p_int << std::endl; }, [](float &p_float) { std::cout << "float: " << p_float << std::endl; }, [](std::string &p_str) { std::cout << "string: " << p_str << std::endl; })}; std::tuple<char, int, float, std::string> _data_0{'k', -19, 8.014, "bye"}; traverse(_functions, _data_0); auto _data_1 = std::make_tuple('k', static_cast<int>(-19), 8.014, std::string("bye")); traverse(_functions, _data_1); }
编译错误信息
main.cpp:46:3: No matching function for call to 'traverse' main.cpp:26:26: candidate template ignored: constraints not satisfied [with t_function = <(lambda at /var/tmp/baires/main.cpp:34:7), (lambda at /var/tmp/baires/main.cpp:35:7), (lambda at /var/tmp/baires/main.cpp:36:7), (lambda at /var/tmp/baires/main.cpp:37:7)>, t_data = <char, int, double, std::basic_string<char>>] main.cpp:24:18: because 'std::is_invocable_r_v<void, (lambda at /var/tmp/baires/main.cpp:36:7), double &>' evaluated to false
核心差异
std::make_tuple会根据传入的实参自动推导类型:你传入的8.014是浮点字面量,默认类型为double,因此_data_1的第三个元素类型是double;而显式声明的_data_0指定了第三个元素为float,编译器会自动把8.014隐式转换为float存入元组。
你的第三个lambda函数签名是[](float &p_float),仅能接受float&类型参数,但_data_1的第三个元素是double,无法绑定到float&,导致std::is_invocable_r_v检查失败,模板约束不满足,从而触发编译错误。
解决办法
你可以通过以下几种方式修复:
显式指定
std::make_tuple的模板参数
强制生成包含float的元组:auto _data_1 = std::make_tuple<char, int, float, std::string>('k', -19, 8.014, "bye");将浮点字面量显式转换为
float
让make_tuple推导第三个元素为float:auto _data_1 = std::make_tuple('k', static_cast<int>(-19), static_cast<float>(8.014), std::string("bye"));放宽lambda的参数类型限制
用auto&让lambda接受任意浮点类型:[](auto &p_num) { std::cout << "float/double: " << p_num << std::endl; },调整模板约束的检查逻辑
若希望允许参数隐式转换,可将约束中的t_data &替换为std::add_lvalue_reference_t<std::decay_t<t_data>>,不过这种方式可能引入潜在问题,需谨慎使用。
内容的提问来源于Stack Exchange,提问作者canellas

