【问题标题】:Lookup table with constexpr使用 constexpr 查找表
【发布时间】:2013-09-25 22:16:11
【问题描述】:

我希望创建一个坐标查找表,例如:

int a[n][2] = {{0,1},{2,3}, ... }

对于给定的n,在编译时创建。我开始研究constexpr,但似乎返回constexpr std::vector<std::array <int, 2> > 的函数不是一个选项,因为我得到了:

invalid return type 'std::vector<std::array<int, 2ul> >' of constexpr function

如何才能创建这样一个编译时数组?

【问题讨论】:

  • std::vector 不是文字类型,因此不能在 C++11 constexpr 中使用。 C++11 的array 类型缺少constexpr 访问器,因此在constexpr 函数中的使用也有限。如果您没有某些 C++1y 库/编译器支持,我建议改用自定义数组类型。
  • @DyP - 你能举个例子吗?
  • 如果你添加一些你想做的事情的细节会更有用;)
  • @DyP - 只需创建一个我想在编译时拥有的坐标列表。例如,一条线上约 100 个点。
  • 注:在 C++1y 中,您还可以使用 initializer_list,因为它们是 required to be literal types in C++1y

标签: c++ c++11 constexpr


【解决方案1】:

使用 C++14,您不需要太多的模板魔法。下面是一个如何使用f(x) = x^3 查找表的示例,其中第一个坐标是x 值,第二个坐标是f(x) 值:

#include <iostream>

template<int N>
struct Table
{
    constexpr Table() : values()
    {
        for (auto i = 0; i < N; ++i)
        {
            values[i][0] = i;
            values[i][1] = i * i * i;
        }
    }
    int values[N][2];
};

int main() {
    constexpr auto a = Table<1000>();
    for (auto x : a.values)
        std::cout << "f(" << x[0] << ") = " << x[1] << '\n';
}

【讨论】:

  • constexpr Table() : values()中的: values()是什么意思?它是零初始化还是什么?
  • &Jonas 是的,它零初始化这是必要的,因为 C++ 需要有一个 compile-const 值。稍后将在构造函数中设置,但编译器很难跟踪
  • @IceFire 你确定吗?根据en.cppreference.com/w/cpp/container/array/operator_at non-const operator[] 仅从 C++17 版本开始为 constexpr。因此,这可能很麻烦
  • @DawidPi 我们这里没有 std::array
  • @IceFire 啊,当然。很抱歉。
【解决方案2】:

我将首先转储代码,稍后在必要/适当的地方添加引用和 cmets。如果结果与您要找的结果有点接近,请发表评论。

包扩展的索引技巧(此处需要应用生成器),由 Xeo 修改为使用 this answer,而不是 unsigned

#include <cstddef>

// by Xeo, from https://stackoverflow.com/a/13294458/420683
template<std::size_t... Is> struct seq{};
template<std::size_t N, std::size_t... Is>
struct gen_seq : gen_seq<N-1, N-1, Is...>{};
template<std::size_t... Is>
struct gen_seq<0, Is...> : seq<Is...>{};

生成器函数:

#include <array>

template<class Generator, std::size_t... Is>
constexpr auto generate_array_helper(Generator g, seq<Is...>)
-> std::array<decltype(g(std::size_t{}, sizeof...(Is))), sizeof...(Is)>
{
    return {{g(Is, sizeof...(Is))...}};
}

template<std::size_t tcount, class Generator>
constexpr auto generate_array(Generator g)
-> decltype( generate_array_helper(g, gen_seq<tcount>{}) )
{
    return generate_array_helper(g, gen_seq<tcount>{});
}

使用示例:

// some literal type
struct point
{
    float x;
    float y;
};
// output support for `std::ostream`
#include <iostream>
std::ostream& operator<<(std::ostream& o, point const& p)
{  return o << p.x << ", " << p.y;  }

// a user-defined generator
constexpr point my_generator(std::size_t curr, std::size_t total)
{
    return {curr*40.0f/(total-1), curr*20.0f/(total-1)};
}

int main()
{
    constexpr auto first_array = generate_array<5>(my_generator);
    constexpr auto second_array = generate_array<10>(my_generator);

    std::cout << "first array: \n";
    for(auto p : first_array)
    {
        std::cout << p << '\n';
    }
    std::cout << "========================\n";

    std::cout << "second array: \n";
    for(auto p : second_array)
    {
        std::cout << p << '\n';
    }
}

【讨论】:

  • 哇——这么多代码!这真的是最短的方法吗?
  • @nbubis 这是一个非常通用的解决方案。如果你缩小/更精确地指定你想要的,可能会有一个不那么冗长的解决方案。
  • 我认为他的使用示例正是我正在寻找的,但这肯定是很多额外的代码:)
  • @nbubis 现在够短了吗?我意识到你甚至不需要这种东西的访问器。
  • +1 here's what I got 在阅读您的问题之前(我对其进行了更新,以使 make_integer_sequence 对呼叫者不透明,就像您所做的那样,tnx 支持这个想法)
【解决方案3】:

使用 GNU gperf 或其他代码生成实用程序怎么样?

【讨论】:

  • 嗨。您可以在编译时使用 gperf 来初始化向量吗?如果是这样呢?
猜你喜欢
  • 1970-01-01
  • 2012-10-22
  • 1970-01-01
  • 2020-12-08
  • 2012-05-22
  • 2022-01-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多