Home > Mobile >  Is it possible to explicitly specify template arguments in a generic lambda passed to a function?
Is it possible to explicitly specify template arguments in a generic lambda passed to a function?

Time:10-05

I want to create a compile-time loop over a small container (like 4-8 elements) with a size known at compile time. It's not hard to create just one simple loop: I can create a template functor F with operator() overloaded and call it like in the code below

constexpr std::array<T, N> array{/*fill the array*/};

template <std::size_t index> struct F
{
    void operator()()
    {
        std::vector<T> some_container{/*fill it in runtime*/};
        some_container[index   some_offset]  = std::get<index>(array); // Use safe array access
    }
};

template <template<std::size_t> typename F, std::size_t... I>
void iterator_implementation(
    const std::index_sequence<I...> /*unused*/)
{
    ((F<I>{}()), ...);
}

template <template<std::size_t> typename F>
void iterator()
{
    iterator_implementation<F>(std::make_index_sequence<array.size()>{});
}

Though, if I want to create ten such loops and want to conveniently pass a bunch of references with it (closure), I should definitely use lambda functions. In order to use std::get in the lambda, I should use generic lambdas. So, the functor from the example below may become

auto f = [&some_container, some_offset]<std::size_t index>()
{
    some_container[index   some_offset]  = std::get<index>(array); // Use safe array access
};

I cannot simply pass this lambda to the iterator because it's impossible to pass a variable of a template type without specifying template parameters, and also I cannot simply get a type of the lambda and provide it as an F in order to call F<I>{} to recreate the lambda. If I use typename F instead of template<std::size_t> typename F, I cannot call f<I> because typename has no template parameters, so cannot be used with them.

I can also pass std::get<index>(array) and index as ordinary arguments to the lambda function and avoid usage of template parameter but saving the safety of the access. Though, I wish to try using compile-time constants wherever it's possible in order to check how it works. I believe that passing a compile-time constant numeric as a template argument may help the compiler to optimize my code more than if the number is an ordinary variable in my code (even though smart compiler should "see" this and optimize the call). So, it's a question about new knowledge rather than discussing whether such an approach will help.

Is it possible to use generic lambda in a fold of parameter pack as I need?

P. S. Despite the question title, it's not a duplicate of How to pass generic lambda into function because in my question template parameter should be specified explicitly, while in that question it can be deduced.

CodePudding user response:

In a generic lambda, operator() is a template, but the lambda type is not.

Instead of instantiating a template at an index F<I>{}(), one needs to instantiate operator() at an index. Since the lambda has captures, one will need to pass it instead of just the type as a template argument.

Replace:

template <template<std::size_t> typename F, std::size_t... I>
void iterator_implementation(
    const std::index_sequence<I...> /*unused*/)
{
    ((F<I>{}()), ...);
}

with:

template <typename F, std::size_t... I>
void iterator_implementation(F f,
    const std::index_sequence<I...> /*unused*/)
{

    ((f.template operator()<I>()), ...);
}

Full example: https://godbolt.org/z/rYTP1KTYc

  • Related