C++20及以上:consteval环境中std::vector转std::array的编译问题
需求与问题代码
想要利用C++20起支持constexpr成员函数的std::vector,在编译期计算数量静态可知但不直观的值,再将这些值写入std::array带入运行时。尝试的代码如下:
#include <array> #include <vector> #include <iostream> consteval std::vector<int> primes_helper(int n) { std::vector<int> v; for (int i = 2; i < n; ++i) { bool prime = true; for (int j = 2; j * j <= i; ++j) { prime = prime && (i % j != 0); if(!prime) break; } if(prime) v.push_back(i); } return v; } template <int N> consteval auto primes() { constexpr auto primes = primes_helper(N); std::array<int, primes.size()> out; for(int i = 0; i < primes.size(); ++i) out[i] = primes[i]; return out; } int main() { constexpr auto p = primes<1000>(); std::cout << p.size() << "\n"; return 0; }
编译时会报错,提示primes()中的primes未用常量表达式初始化,需要明确错误原因及修复方法。
错误原因
核心问题在于:std::vector不是字面类型(literal type),而constexpr变量要求其类型必须是字面类型,同时初始化表达式必须是常量表达式。哪怕primes_helper是consteval函数(确保返回值是编译期常量),也不能用constexpr来声明std::vector类型的变量——因为std::vector内部包含动态分配的内存,不符合字面类型的定义(字面类型要求所有成员都是字面类型,且能在编译期完成构造/销毁)。
修复方案
在consteval函数内部,所有变量的求值都强制在编译期完成,不需要给std::vector变量加constexpr修饰。只需要把constexpr auto primes = primes_helper(N);改成auto primes = primes_helper(N);即可:
#include <array> #include <vector> #include <iostream> consteval std::vector<int> primes_helper(int n) { std::vector<int> v; for (int i = 2; i < n; ++i) { bool prime = true; for (int j = 2; j * j <= i; ++j) { prime = prime && (i % j != 0); if(!prime) break; } if(prime) v.push_back(i); } return v; } template <int N> consteval auto primes() { auto primes = primes_helper(N); // 去掉constexpr修饰 std::array<int, primes.size()> out; for(int i = 0; i < primes.size(); ++i) out[i] = primes[i]; return out; } int main() { constexpr auto p = primes<1000>(); std::cout << p.size() << "\n"; return 0; }
关于另一个可行版本的说明
那个先调用助手函数获取数组大小再生成数组的版本之所以能运行,是因为它把std::vector的使用限制在了模板参数推导的常量表达式上下文里:先通过consteval函数得到大小(此时primes_helper(N).size()是编译期常量,可用于模板参数),之后生成std::array时不再持有std::vector变量,避开了“用constexpr声明非字面类型变量”的问题。
内容的提问来源于stack exchange,提问作者user2426460

