user13507303
user13507303

Reputation:

C++: candidate template ignored when iterating over tuple

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

Answers (2)

user13507303
user13507303

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

super
super

Reputation: 12938

In your call to

consume_all<Fs...>

you are missing I. You would need

consume_all<0, Fs...>

Upvotes: 1

Related Questions