Constexpr лямбда аргумент - PullRequest
2 голосов
/ 08 июля 2019

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

ForEach Функция, представленная ниже, вызывает данную лямбду 3 раза с индексом 0, 1, 2:

template <class Func, std::size_t... index>
inline constexpr void ForEach(Func && f, std::index_sequence<index...>)
{
    (f(index), ...);
}

template <class Func>
inline constexpr void ForEach(Func && f)
{
    ForEach(f, std::make_index_sequence<3>());
}

поэтому следующий код

ForEach([](size_t index)
{
    std::cout << index << ' ' << std::endl;
});

выходы 0, 1, 2.

Но следующий код, который пытается напечатать элементы кортежа, требует, чтобы index был constexpr:

auto t = std::make_tuple(1, 2.0, std::string("abc"));

ForEach([&t](size_t index)
{
    std::cout << std::get<index>(t) << ' ' << std::endl;
});

и, следовательно, не компилируется, см. живой пример . Можно ли как-нибудь сделать index constexpr?

EDIT1: Существует рабочий пример , где в качестве аргумента шаблона используется лямбда-аргумент:

void Set(Tuple& val, size_t index, Variant const& elem_v)
{
    mp_with_index<std::tuple_size_v<Tuple>>(
        index,
        [&](auto I){
            std::visit([&](auto const& alt){
                if constexpr (std::is_assignable_v<
                        std::tuple_element_t<Tuple, I>,
                        decltype(alt)>)
                {
                    std::get<I>(val) = alt;
                } else {
                    throw /* something */;
                }
            }, elem_v);
        });
}

почему это компилируется, а мой пример кода нет?

1 Ответ

7 голосов
/ 08 июля 2019

В этом:

ForEach([&t](size_t index)
{
    std::cout << std::get<index>(t) << ' ' << std::endl;
});

index не является константным выражением. Это просто переменная. Параметры функции не являются constexpr.

Но если мы немного подправим ForEach (чтобы работать так же, как мой пример, который вы связали):

template <class Func, std::size_t... index>
inline constexpr void ForEach(Func && f, std::index_sequence<index...>)
{
    (f(std::integral_constant<std::size_t, index>()), ...);
    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    //    instead of just index
}

ForEach([&t](auto index)
{
    std::cout << std::get<index>(t) << ' ' << std::endl;
});

Тогда это работает, потому что index больше не size_t, а скорее разные экземпляры std::integral_constant<size_t, V> для различных V. Этот тип выглядит что-то вроде :

template<class T, T v>
struct integral_constant {
    static constexpr T value = v;
    typedef T value_type;
    typedef integral_constant type; // using injected-class-name
    constexpr operator value_type() const noexcept { return value; }
    constexpr value_type operator()() const noexcept { return value; } //since c++14
};

Преобразование std::integral_constant<size_t, V> в size_t вызывает constepxr operator size_t(), который не предполагает чтения какого-либо состояния из самого объекта (который является пустым типом), следовательно, он разрешен как константное выражение.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...