有没有一种方法可以将`constexpr`值传递给lambda,以使其在该lambda中保持`constexpr`?

时间:2018-11-28 15:22:38

标签: c++ templates c++17 template-meta-programming constexpr

这就是我想做的;发布整个代码,因为它不太长,并且还演示了我要解决的特定任务。基本上,我需要一种按索引迭代参数包中的值的方法(即使在此示例中不需要索引部分,索引部分也很重要)。

#include <iostream>
#include <tuple>
#include <type_traits>

template <int First, int Last, typename Functor>
constexpr void static_for(Functor&& f)
{
    if constexpr (First < Last)
    {
        f(std::integral_constant<int, First>{});
        static_for<First + 1, Last, Functor>(std::forward<Functor>(f));
    }
}

template <size_t index, typename... Args>
auto value_by_index(Args&&... args) noexcept {
    return std::get<index>(std::forward_as_tuple(std::forward<Args>(args)...));
}

template <typename... ValueTypes>
void traverse(ValueTypes... values)
{
    static_for<0, sizeof...(ValueTypes)>([&](int i) {
        auto v = value_by_index<static_cast<size_t>(i), ValueTypes...>(values...);
        std::cout << v << std::endl;
    });
}

int main()
{
    traverse(0.0f, 1, 3.33, "str");

    return 0;
}

编译器错误当然是:

<source>:24:71: error: 'i' is not a constant expression

如果lambda可以具有显式的模板参数,则i就是这样的参数,并且对于编译器来说,它在编译时是已知的。但这不是lambda的工作方式。

如果您想将其视为XY问题,我想我不需要专门在static_for内调用lambda,但是我确实需要调用一些可以访问参数包的代码(按索引的traverse中的s),如果traverse是成员函数,则需要访问其this

在线尝试:https://godbolt.org/z/eW4rnm

2 个答案:

答案 0 :(得分:7)

使用通用lambda和constexpr转换运算符

template <typename... ValueTypes>
void traverse(ValueTypes... values)
{
    static_for<0, sizeof...(ValueTypes)>([&](auto I)
    //                                       ~~~^
    {
        auto v = value_by_index<I>(values...);
        //                     ~^~
        std::cout << v << std::endl;
    });
}

DEMO

对lambda表达式使用模板参数列表:

template <typename... ValueTypes>
void traverse(ValueTypes... values)
{
    static_for<0, sizeof...(ValueTypes)>([&]<int I>(std::integral_constant<int, I>)
    //                                       ~~~~^                             ~^~
    {
        auto v = value_by_index<I>(values...);
        //                     ~^~
        std::cout << v << std::endl;
    });
}

DEMO 2

答案 1 :(得分:4)

玩太晚了吗?

  

基本上,我需要一种通过索引迭代参数包中的值的方法(索引部分很重要,即使在此示例中不需要)。

抱歉,... std::make_index_sequencestd::index_sequence的旧用法怎么样?

维护您的value_by_index(),我提出了以下基于traverse()traverse_helper()的C ++ 14解决方案

template <typename F, std::size_t ... Is, typename ... VTs>
void traverse_helper (F f, std::index_sequence<Is...>, VTs ... vs)
 { 
   using unused = int[];

   (void)unused { 0, (f(value_by_index<Is>(vs...)), 0)... };
 }

template <typename F, typename ... VTs>
void traverse (F f, VTs ... vs)
 { traverse_helper(f, std::make_index_sequence<sizeof...(VTs)>{}, vs...); }

观察到我也传递了callable作为参数。

如果您可以使用C ++ 17(如您标记的那样),traverse_helper()就会变得

template <typename F, std::size_t ... Is, typename ... VTs>
void traverse_helper (F f, std::index_sequence<Is...>, VTs ... vs)
 { (f(value_by_index<Is>(vs...)), ...); }

您可以按以下方式致电traverse()

traverse([](auto x){ std::cout << x << std::endl; },
         0.0f, 1, 3.33, "str");

以下是完整的C ++ 14编译示例

#include <iostream>
#include <tuple>
#include <type_traits>

template <std::size_t I, typename ... As>
auto value_by_index (As && ... as) noexcept
 { return std::get<I>(std::forward_as_tuple(std::forward<As>(as)...)); }

template <typename F, std::size_t ... Is, typename ... VTs>
void traverse_helper (F f, std::index_sequence<Is...>, VTs ... vs)
 { 
   using unused = int[];

   (void)unused { 0, (f(value_by_index<Is>(vs...)), 0)... };
 }

template <typename F, typename ... VTs>
void traverse (F f, VTs ... vs)
 { traverse_helper(f, std::make_index_sequence<sizeof...(VTs)>{}, vs...); }

int main ()
 {
    traverse([](auto x){ std::cout << x << std::endl; },
             0.0f, 1, 3.33, "str");
 }