Reputation:
I am attempting to iterate through a tuple using the following code:
template <std::size_t I = 0, typename... Ts>
requires (I >= sizeof...(Ts))
static inline auto consume_all(std::tuple<Ts...>&&, auto) -> void {}
template <std::size_t I = 0, typename... Ts>
requires (I < sizeof...(Ts))
static inline auto consume_all(std::tuple<Ts...>&& tup, auto f) -> void {
f(std::get<I>(tup));
consume_all<I + 1, Ts...>(tup, f);
}
From what I can see there is nothing wrong with the above definition. However when I try to call it...:
template <typename F, typename T>
concept unary = requires(F&& f, T&& t) {
{
f(t)
} -> std::convertible_to<T>;
};
//...
//in struct definition:
const std::tuple<Fs...> funcs;
//...
//in method definition:
consume_all<Fs...>(
funcs,
[=, &t]<unary<T> F>(F f) {t = f(t);}
);
...I receive a compile time error saying that both templates have been ignored:
No matching function for call to 'consume_all' in instantiation of member function 'chain<int, [function-typenames]>'
...
candidate template ignored: invalid explicitly-specified argument for template parameter 'I'
candidate template ignored: invalid explicitly-specified argument for template parameter 'I'
I cannot see the issue as the combination of ... >= I
and ... < I
should be all encompassing.
Edit:
When specifying I
(consume_all<0, Fs...>
) the candidate templates are still ignored although now they yield a more descriptive error:
No matching function for call to 'consume_all'
...
candidate template ignored: //issue
deduced type 'tuple<...>' of 1st parameter does not match adjusted type
'const tuple<...>' of argument
[with I = 0, Ts = <(lambda)>, f:auto = (lambda)]
candidate template ignored: //rightfully
constraints not satisfied
[with I = 0, Ts = <(lambda)>, auto:3 = (lambda)]
because '0U >= sizeof...(Ts)' (0 >= 1) evaluated to false
Upvotes: 0
Views: 309
Reputation:
After looking at @Jarod42 's comment I have found a better way to iterate through a tuple that side-steps the template issue.
Defining consume_all
as an iterative function is not the way to go.
Instead consume_all
should be defined using std::apply(func, tup)
which parses the tuple tup
as the argument for func
.
This now converts the problem of iterating through a tuple into that of iterating through a parameter pack (which is easier).
Iterating through the elements of a parameter pack:
template <typename F>
[[maybe_unused]] static inline void consume(F) {}
//^^^ Empty Base Case ^^^
template <typename F, typename Arg>
requires std::invocable<F, Arg>
[[maybe_unused]] static inline void consume(F f, Arg arg) {
f(arg);
}
//^^^ Single Arg Base Case ^^^
template <typename F, typename Arg, typename... Args>
requires std::invocable<F, Arg>
static inline void consume(F f, Arg arg, Args... args) {
f(arg);
consume<F, Args...>(f, args...);
}
//^^^ Recursive General Case ^^^
Now that we have defined a function for consuming each element of a parm pack we can now properly define consume_all
Redefining consume_all:
template <typename F, typename... Args>
[[maybe_unused]] static inline void consume_all(F f, std::tuple<Args...> tup) {
std::apply(
[&f](auto&&... args){
consume(f, args...);
},
tup
);
}
Solving the original problem:
consume_all(
[&t]<unary<T> F>(F f) {t = f(t);},
funcs
);
I hope this helps anyone else with a similar problem
Upvotes: 0
Reputation: 12938
In your call to
consume_all<Fs...>
you are missing I
. You would need
consume_all<0, Fs...>
Upvotes: 1