Возможно ли иметь лямбду с аргументом 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?
РЕДАКТИРОВАТЬ1: Существует рабочий пример, где лямбда-аргумент используется в качестве аргумента шаблона:
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);
});
}
почему это компилируется, а мой пример кода нет?
@Fureeish, см. EDIT1. Почему он компилируется?
@AlexeyStarinsky I — это std::integral_constant<std::size_t, (something)>, у которого есть constexpr operator std::size_t(), который возвращает это (something)
@Fureeish Это не так.





В этом:
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(), что не требует считывания какого-либо состояния из самого объекта (который является пустым типом), поэтому оно разрешено как константное выражение.
Другой взгляд на это заключается в том, что мы кодируем значение в тип (которое может быть получено как константное выражение), а не в стоимость (что невозможно).
Нет. Аргументы функции нельзя рассматривать как
constexpr(не уверен, меняет ли этоC++20).