Constexpr Lambda参数

Ale*_*sky 2 c++ c++17

是否可以使用带有constexpr参数的lambda?是否可以使以下示例正常工作?

ForEach 下面提供的函数用索引0、1、2调用给定的lambda 3次:

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>());
}
Run Code Online (Sandbox Code Playgroud)

所以下面的代码

ForEach([](size_t index)
{
    std::cout << index << ' ' << std::endl;
});
Run Code Online (Sandbox Code Playgroud)

输出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;
});
Run Code Online (Sandbox Code Playgroud)

因此无法编译,请参见在线示例。是否可以以index某种方式使constexpr?

EDIT1:有一个工作示例,其中lambda参数用作模板参数:

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);
        });
}
Run Code Online (Sandbox Code Playgroud)

为什么要编译,但我的示例代码不能编译?

Bar*_*rry 7

In this:

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

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;
});
Run Code Online (Sandbox Code Playgroud)

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
};
Run Code Online (Sandbox Code Playgroud)

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).