Is it possible to have a lambda with a constexpr argument? And is it possible to make the following example work?
ForEach function provided below calls a given lambda 3 times with index 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>());
}
so the following code
ForEach([](size_t index)
{
std::cout << index << ' ' << std::endl;
});
outputs 0, 1, 2.
But the following code that tries to print tuple elements requires index to be a 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;
});
and thus does not compile, see live example. Is it possible to make index constexpr somehow?
EDIT1: There is a working example where a lambda argument is used as a template argument:
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);
});
}
why does this compile, but my sample code does not?
In this:
ForEach([&t](size_t index)
{
std::cout << std::get<index>(t) << ' ' << std::endl;
});
index is not a constant expression. It's just a variable. Function parameters are not constexpr.
But if we tweaked ForEach somewhat (to work the same way as the example of mine that you linked):
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;
});
Then this works because index is no longer size_t but rather different instances of std::integral_constant<size_t, V> for various V. That type looks something like:
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 };
Converting a std::integral_constant<size_t, V> to a size_t invokes the constepxr operator size_t(), which doesn't involve reading any state from this object itself (which is an empty type), hence it's allowed as a constant expression.
A different way of looking at it is that we're encoding the value in the type (which can be retrieved as a constant expression) rather than in the value (which cannot).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With