简体   繁体   English

如何存储编译时动态分配的内存以便可以在运行时使用?

[英]How do I store the compile-time dynamically allocated memory so that it can be used in run-time?

Say, I have a constexpr variable that contains all primes less than 2 16 .比如说,我有一个constexpr变量,其中包含所有小于 2 16的素数。

constexpr auto primes = [] {
    constexpr int N = 1 << 16;
    std::array<int, 6542> ret;
    bool not_prime[N] = {};
    int prime_cnt = 0;
    for (int i = 2; i < N; i++) {
        if (!not_prime[i]) {
            ret[prime_cnt++] = i;
            for (int j = 2 * i; j < N; j += i) not_prime[j] = true;
        }
    }
    return ret;
}();

Here I manually specified the ret 's array size.这里我手动指定了ret的数组大小。 When I want to change N , I have to re-run this loop to see what the final prime_cnt value is and manually specify it.当我想更改N时,我必须重新运行此循环以查看最终prime_cnt值是什么并手动指定它。 Particularly, if I increase N , I have to first give an upper-bound guess to avoid segfault.特别是,如果我增加N ,我必须首先给出一个上限猜测以避免段错误。

In C++20, we can dynamically allocate memory in compile-time, and thus we have constexpr vector now.在 C++20 中,我们可以在编译时动态分配内存,因此我们现在有了constexpr向量。 So we no longer need to worry about the upper-bound problem.所以我们不再需要担心上限问题。 But such a value cannot be used in run-time, which means, this code is invalid.但是这样的值不能在运行时使用,也就是说,这段代码是无效的。

constexpr auto primes = [] {
    constexpr int N = 1 << 16;
    std::vector<int> ret;
    bool not_prime[N] = {};
    for (int i = 2; i < N; i++) {
        if (!not_prime[i]) {
            ret.push_back(i);
            for (int j = 2 * i; j < N; j += i) not_prime[j] = true;
        }
    }
    return ret;  // invalid here
}();

So here comes the problem, how do I store such a vector?那么问题来了,我如何存储这样的向量?

An obvious solution is to split this process into two phases.一个明显的解决方案是将这个过程分成两个阶段。 The first one calculates the array size and the second one does the normal calculation.第一个计算数组大小,第二个进行正常计算。 But this needs extra code and computing resources.但这需要额外的代码和计算资源。 Is there an elegant and automatic way to achieve this goal?有没有一种优雅而自动的方式来实现这个目标?

Create a lambda that makes a vector , and use another lambda to create an array创建一个 lambda 来创建一个vector ,并使用另一个 lambda 来创建一个array

#include <array>
#include <vector>
#include <algorithm>

constexpr auto primes_num_vector = [] {
  constexpr int N = 1 << 16;
  std::vector<int> ret;
  bool not_prime[N] = {};
  for (int i = 2; i < N; i++) {
    if (!not_prime[i]) {
      ret.push_back(i);
      for (int j = 2 * i; j < N; j += i) not_prime[j] = true;
    }
  }
  return ret;
};

constexpr auto primes = [] {
  std::array<int, primes_num_vector().size()> ret;
  std::ranges::copy(primes_num_vector(), ret.begin());
  return ret;
}();

A std::vector uses dynamic allocations, and in a constexpr any dynamic allocation must be matched by delete . std::vector使用动态分配,并且在constexpr中,任何动态分配都必须与delete匹配。 So you cannot constexpr a std::vector like that.所以你不能像这样constexpr一个std::vector

But nothing stops you from using std::array .但是没有什么能阻止你使用std::array The number of primes below N can be counted in a constexpr , and then you can use that to give a compile-time size for the std::array :可以在constexpr中计算低于N的素数的数量,然后您可以使用它来为std::array提供编译时大小:

#include <vector>
#include <array>

constexpr std::size_t num_primes(std::size_t N) {
    std::size_t num = N - 2;
    for (std::size_t i = 2; i < N; ++i) {
        for (std::size_t j = 2; j * j <= i; ++j) {
            if (i % j == 0) {
                --num;
                break;
            } 
        }
    }
    return num;
}

constexpr auto primes = [] {
    constexpr int N = 1 << 16;
    std::array<std::size_t, num_primes(N)> ret;
    std::size_t pos = 0;
    bool not_prime[N] = {};
    for (int i = 2; i < N; i++) {
        if (!not_prime[i]) {
            ret[pos++] = i;
            for (int j = 2 * i; j < N; j += i) not_prime[j] = true;
        }
    }
    return ret;  // invalid here no more
}();

#include <iostream>
int main() {
    for (const auto & x : primes) {
        std::cout << " " << x;
    }
    std::cout << std::endl;
}

The code is pretty stupid as it computes all primes twice, once with the horrible inefficient division test, and once with a sieve.该代码非常愚蠢,因为它计算所有素数两次,一次使用可怕的低效除法测试,一次使用筛子。 Optimizing this is left to the reader.优化这个留给读者。

Tip: merge the two functions, count primes while you create the sieve.提示:合并这两个函数,在创建筛子时计算素数。 Then create the array and fill it by iterating over the sieve.然后创建数组并通过迭代筛子来填充它。 Or, create a vector during sieve creation, and then copy the vector into the array.或者,在筛子创建过程中创建一个向量,然后将该向量复制到数组中。 As long as the vector is destroyed in the constexpr , the whole is a constexpr .只要向量在constexpr中被销毁,整体就是一个constexpr

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM