Есть ли способ передать значение «constexpr» в лямбду, чтобы оно оставалось «constexpr» внутри этой лямбды? - PullRequest
0 голосов
/ 28 ноября 2018

Вот что я хочу сделать;опубликовать весь код, потому что он не слишком длинный, а также продемонстрировать конкретную задачу, которую я пытаюсь решить.По сути, мне нужен способ итерации значений из пакета параметров по индексу (индексная часть важна, хотя в этом примере это не требуется).

#include <iostream>
#include <tuple>
#include <type_traits>

template <int First, int Last, typename Functor>
constexpr void static_for(Functor&& f)
{
    if constexpr (First < Last)
    {
        f(std::integral_constant<int, First>{});
        static_for<First + 1, Last, Functor>(std::forward<Functor>(f));
    }
}

template <size_t index, typename... Args>
auto value_by_index(Args&&... args) noexcept {
    return std::get<index>(std::forward_as_tuple(std::forward<Args>(args)...));
}

template <typename... ValueTypes>
void traverse(ValueTypes... values)
{
    static_for<0, sizeof...(ValueTypes)>([&](int i) {
        auto v = value_by_index<static_cast<size_t>(i), ValueTypes...>(values...);
        std::cout << v << std::endl;
    });
}

int main()
{
    traverse(0.0f, 1, 3.33, "str");

    return 0;
}

Ошибка компилятора, конечно, такова:

<source>:24:71: error: 'i' is not a constant expression

Если бы лямбды могли иметь явные аргументы шаблона, i был бы таким аргументом, и компилятору было бы очевидно, что он известен во время компиляции.Но это не то, как работают лямбды.

Если вы хотите рассматривать это как проблему XY, я полагаю, мне не нужно специально вызывать лямбду внутри моего static_for, но мне нужно вызвать некоторую частькода, который может получить доступ к пакету (-ам) параметров traverse по индексу, и если traverse была функцией-членом, мне нужно иметь доступ к ее this.

Попробуйте онлайн: https://godbolt.org/z/eW4rnm

Ответы [ 2 ]

0 голосов
/ 28 ноября 2018

Слишком поздно играть?

По сути, мне нужен способ перебора значений из пакета параметров по индексу (индексная часть важна, даже если в этом примере это не требуется).

Извините, но ... как насчет старого доброго использования std::make_index_sequence и std::index_sequence?

Поддерживая ваше value_by_index(), я предлагаю следующее решение на C ++ 14 на основена traverse() с traverse_helper()

template <typename F, std::size_t ... Is, typename ... VTs>
void traverse_helper (F f, std::index_sequence<Is...>, VTs ... vs)
 { 
   using unused = int[];

   (void)unused { 0, (f(value_by_index<Is>(vs...)), 0)... };
 }

template <typename F, typename ... VTs>
void traverse (F f, VTs ... vs)
 { traverse_helper(f, std::make_index_sequence<sizeof...(VTs)>{}, vs...); }

Обратите внимание, что я передал также вызываемый объект в качестве параметра.

Если вы можете использовать C ++ 17 (как вы отметили), traverse_helper() просто становится

template <typename F, std::size_t ... Is, typename ... VTs>
void traverse_helper (F f, std::index_sequence<Is...>, VTs ... vs)
 { (f(value_by_index<Is>(vs...)), ...); }

Вы можете позвонить traverse() следующим образом

traverse([](auto x){ std::cout << x << std::endl; },
         0.0f, 1, 3.33, "str");

Ниже приведен полный пример компиляции C ++ 14

#include <iostream>
#include <tuple>
#include <type_traits>

template <std::size_t I, typename ... As>
auto value_by_index (As && ... as) noexcept
 { return std::get<I>(std::forward_as_tuple(std::forward<As>(as)...)); }

template <typename F, std::size_t ... Is, typename ... VTs>
void traverse_helper (F f, std::index_sequence<Is...>, VTs ... vs)
 { 
   using unused = int[];

   (void)unused { 0, (f(value_by_index<Is>(vs...)), 0)... };
 }

template <typename F, typename ... VTs>
void traverse (F f, VTs ... vs)
 { traverse_helper(f, std::make_index_sequence<sizeof...(VTs)>{}, vs...); }

int main ()
 {
    traverse([](auto x){ std::cout << x << std::endl; },
             0.0f, 1, 3.33, "str");
 }
0 голосов
/ 28 ноября 2018

Используйте обобщенную лямбду и оператор преобразования constexpr :

template <typename... ValueTypes>
void traverse(ValueTypes... values)
{
    static_for<0, sizeof...(ValueTypes)>([&](auto I)
    //                                       ~~~^
    {
        auto v = value_by_index<I>(values...);
        //                     ~^~
        std::cout << v << std::endl;
    });
}

DEMO

Используйте список параметров шаблона для лямбда-выражения :

template <typename... ValueTypes>
void traverse(ValueTypes... values)
{
    static_for<0, sizeof...(ValueTypes)>([&]<int I>(std::integral_constant<int, I>)
    //                                       ~~~~^                             ~^~
    {
        auto v = value_by_index<I>(values...);
        //                     ~^~
        std::cout << v << std::endl;
    });
}

DEMO 2

...