为什么C++常量模板化向量在使用之前没有初始化?

Why isn't that C++ const templated vector initialised before being used?

本文关键字:初始化 常量 C++ 向量 为什么      更新时间:2023-10-16

更新:问题是为什么下面的代码(MWE(按原样工作,而不是像我期望的那样工作。

为了个人方便,我创建了以下模板化向量:

// 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>长度为二。

由(非显式(模板专用化产生的非本地静态存储持续时间变量的动态初始化是无序的,即不确定地排序,这意味着初始化发生的顺序是未指定的。它既不考虑变量之间的依赖关系,也不考虑定义的顺序或实例化的顺序。

因此你的程序有未定义的行为,因为Data<2>::elems,从main中的使用实例化,具有无序的动态初始化,并使用range<2>range<3>,两者都有无序的动态初始化。由于未指定是先初始化前者还是后者,因此您可能会在初始化开始之前访问range<2>range<3>,从而导致未定义的行为。

这可以通过使用std::array而不是std::vectorforrange及其初始值设定项(并删除初始值设定项中的cout语句(来解决,以便初始值设定项成为常量表达式。然后range<K>将没有动态初始化,而是常量初始化,它总是在任何动态初始化之前执行,即在Data<K>::elems使用它之前。

此外,还应将range声明为constexpr,以确保初始值设定项确实是一个常量表达式。否则,您可能仍会在没有警告的情况下获得动态初始化和未定义的行为,例如,当您所做的更改意外导致初始值设定项不再是常量表达式时。