首页 > 解决方案 > Consexpr lambda 参数

问题描述

是否可以有一个带有 constexpr 参数的 lambda?是否可以使以下示例起作用?

ForEach下面提供的函数调用给定的 lambda 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;
});

因此无法编译,请参见live example。有可能以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);
        });
}

为什么这个编译,但我的示例代码没有?

标签: c++c++17

解决方案


在这:

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不再是for varioussize_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
};

将 a 转换std::integral_constant<size_t, V>为 asize_t调用constepxr operator size_t(),这不涉及从该对象本身(它是一个空类型)读取任何状态,因此它被允许作为一个常量表达式。

另一种看待它的方式是,我们将值编码为类型(可以作为常量表达式检索)而不是(不能)。


推荐阅读