can*_*las 2 c++ template-meta-programming c++-concepts
My goal is to have a generic way to traverse a std::tuple, and the following code tries to show it:
#include <iostream>
#include <tuple>
template <typename t, size_t t_idx, typename t_tuple>
concept visit_tuple_element_value = requires(t &&p_t, const t_tuple &p_tuple) {
{
p_t.template operator()<t_idx>(
std::declval<std::add_const_t<std::add_lvalue_reference_t<t_tuple>>>())
} -> std::same_as<bool>;
};
template <typename t_tuple, typename t_function, size_t t_idx = 0>
requires(visit_tuple_element_value<t_function, t_idx, t_tuple>)
void traverse_tuple_values(t_function p_function, const t_tuple &p_tuple) {
if constexpr (t_idx < std::tuple_size_v<t_tuple>) {
if (p_function.template operator()<t_idx>(p_tuple)) {
traverse_tuple_values<t_tuple, t_function, t_idx + 1>(p_function,
p_tuple);
}
}
}
struct a {};
struct b {};
struct c {};
using my_tuple = std::tuple<b, a, c>;
int main() {
my_tuple _tuple;
auto _visitor = [&]<size_t t_idx>(const my_tuple & /*p_tuple*/) {
if constexpr (std::is_same_v<std::tuple_element_t<t_idx, my_tuple>, a>) {
std::cout << "'a' is at index " << t_idx << std::endl;
} else {
std::cout << "'a' is NOT at index " << t_idx << std::endl;
}
return true;
};
traverse_tuple_values(_visitor, _tuple);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
I am getting the error:
'traverse_tuple_values<std::tuple<b, a, c>, (lambda at main.cpp:63:19), 3UL>'
Run Code Online (Sandbox Code Playgroud)
I do not understand why p_function.template operator()<t_idx>(p_tuple), and therefore _visitor, is being called with t_idx equals to 3, if I have in traverse_tuple_values the line if constexpr (t_idx < std::tuple_size_v<t_tuple>).
g++ --version reports g++ (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0
You check if constexpr (t_idx < std::tuple_size_v<t_tuple>). But when index 2 is reached, the compiler tries to instantiate traverse_tuple_values<..., 3> because t_idx + 1 == 2. Then the compiler checks the constraints for the function but visit_tuple_element_value with t_idx = 3 fails.
To solve your problem, you could check for a termination criterion, e.g. if constexpr (t_idx >= std::tuple_size_v<t_tuple>) and not call itself in this case. Then you don't need the function you want to apply to all elements to return a bool and the concept is not needed anymore either.
Or you can be more explicit with fold expressions:
template<typename Func, typename... Args>
void traverse_tuple_values_with_index(Func function, const std::tuple<Args...>& tup) {
[&]<std::size_t ... Is>(std::index_sequence<Is...>) {
(function.template operator()<Is>(tup), ...);
}(std::make_index_sequence<sizeof...(Args)>());
}
Run Code Online (Sandbox Code Playgroud)
this code directly calls function once with all indices as template argument.
Alternatively you can remove the need for the index and operate on the element instead on the tuple + index;
template<typename Func, typename... Args>
void traverse_tuple_values(Func function, const std::tuple<Args...>& tup) {
[&]<std::size_t ... Is>(std::index_sequence<Is...>) {
(function(std::get<Is>(tup)), ...);
}(std::make_index_sequence<sizeof...(Args)>());
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
112 次 |
| 最近记录: |