c++ - 为什么在使用之前没有初始化 C++ const 模板化向量?
问题描述
更新:问题是为什么下面的代码(MWE)按原样工作,而不是我期望的那样。
为了个人方便,我创建了以下模板化向量 const:
// shorthand for loops, etc.
template <size_t N>
const vector<size_t> range = []() {
vector<size_t> res(N);
for (size_t i = 0; i < N; i++) res[i] = i;
cout << "Created range<" << N << ">: [";
for (auto x: res) cout << x << ' ';
cout << ']' << endl;
return res;
}();
因此,我可以编写更简洁的循环,如下所示:
for (auto i : range<42>) do_something(i);
但是,我意识到(经过一些调试)似乎不能保证所有必需的实例range<N>
在使用前都被初始化!这是相当违反直觉的,所以我想如果我做错了什么。
更准确地说,我有以下 MWE:
#include <bits/stdc++.h>
using namespace std;
template <size_t N>
const vector<size_t> range = []() {
cout << "Initialising range<" << N << ">" << endl;
vector<size_t> result(N);
for (size_t i = 0; i < N; i++) result[i] = i;
return result;
}();
template <size_t K>
class Data {
private:
size_t m_code;
public:
size_t get_code() const { return m_code; }
constexpr static size_t cardinality = K + 1;
explicit Data(size_t code);
const static vector<Data> elems;
};
template <size_t K>
const vector<Data<K>> Data<K>::elems = []() {
cout << "Creating Data elements for K=" << K << endl;
vector<Data<K>> xs;
for (size_t i : range<Data<K>::cardinality>) xs.push_back(Data<K>(i));
return xs;
}();
template <size_t K>
Data<K>::Data(size_t code) {
m_code = code;
cout << "At the moment, range<" << K << "> is [";
for (auto k : range<K>)
cout << k << ' '; // <<< Shouldn't range<K> be already initialised here?..
cout << "] (len=" << range<K>.size() << ")" << endl;
}
int main() {
cout << ">>> Inside main()" << endl;
constexpr size_t K = 2;
cout << "Data elements:" << endl;
for (const auto &X : Data<K>::elems) {
cout << "Element Data(" << X.get_code() << ")" << endl;
}
cout << "Now, range<" << K << "> is [";
for (auto k : range<K>) cout << k << ' ';
cout << "] (len=" << range<K>.size() << ")" << endl;
}
这会产生以下输出:
Initialising range<3>
Creating Data elements for K=2
At the moment, range<2> is [] (len=0)
At the moment, range<2> is [] (len=0)
At the moment, range<2> is [] (len=0)
Initialising range<2>
>>> Inside main()
Data elements:
Element Data(0)
Element Data(1)
Element Data(2)
Now, range<2> is [0 1 ] (len=2)
我真的不明白为什么它会这样工作。我的意思是,我希望一个const
向量(或任何向量!)在使用之前被初始化,因此range<2>
在我在代码中使用它的任何时候它的长度都是 2。
解决方案
由(非显式)模板特化产生的非局部静态存储持续时间变量的动态初始化是无序的,即不确定地排序,这意味着初始化发生的顺序是未指定的。它不考虑变量之间的依赖关系、定义顺序或实例化顺序。
因此,您的程序具有未定义的行为,因为Data<2>::elems
从使用 in 实例化main
,具有无序的动态初始化和使用range<2>
,并且range<3>
两者也具有无序的动态初始化。由于未指定是先初始化前者还是后者,因此您可能访问range<2>
或range<3>
在它们的初始化开始之前访问,从而导致未定义的行为。
这可以通过在其初始化程序中使用std::array
而不是std::vector
for range
and 来解决(并删除初始化程序cout
中的语句),以便初始化程序成为一个常量表达式。thenrange<K>
不会有动态初始化,而是常量初始化,它总是在任何动态初始化之前执行,即 beforeData<K>::elems
将使用它。
此外,您应该声明range
asconstexpr
以确保初始化程序确实是一个常量表达式。否则,您可能仍然会在没有警告的情况下获得动态初始化和未定义的行为,例如,当您做出意外导致初始化程序不再是常量表达式的更改时。
推荐阅读
- java - 屏蔽 CXF 记录 SOAP 请求中的敏感数据
- python - Python - 导入类模块时出现名称错误
- javascript - 为什么我的复选框在点击后没有更新?
- reactjs - 访问令牌的 Facebook 登录问题
- http - CMD/Batch - cURL HTTP 响应代码的 IF 条件
- javascript - 如何在 MongoDB 中保存多行代码片段,检索并以 HTML 格式显示它们
- c# - 获取随机n个数字的序列,这些数字加起来但仅来自一组特定的数字
- c# - 变量类型字典值的 C# 数据设计
- java - 在 ubuntu 上安装 java 13 返回 404 not found 错误
- sql-server - 实体框架 6 中是否识别 SQL Server 标量值?