构造函数初始值设定项列表中长度未知的数组
Array of unknown length in constructor initializer list
我有一个带有成员数组的 class。长度是一个常量,但是这个常量直到编译时才知道(在我的实际代码中,这个常量对于不同的编译目标有不同的定义)。数组的类型是 class,没有默认构造函数。
#define CONSTANT 2
class Data {
public:
Data(int number){}
};
class DemoClass {
private:
Data _member[CONSTANT];
public:
DemoClass():
_member{
Data(0),
Data(0)
}
{
// stuff
}
};
在这个例子中,我可以使用初始化列表设置_member
。但是,如果 COSNTANT
的值发生变化,我必须更改该初始化列表。
理论上,将 DemoClass
更改为具有调用另一个参数为 0
的构造函数的默认构造函数适用于我的情况,因为我将始终调用 Data
0
的构造函数。但是,我无法更改 DemoClass
,因为它在外部库中。
我考虑过的一个解决方案是创建以下 class:
class CustomData : public Data {
public:
CustomData() : Data(0){}
};
这行得通,但似乎有点复杂。有没有更简单的方法来初始化这个数组?
一个简单的解决方案是使用 std::vector
。这显然确实有引入动态分配的缺点:
std::vector<Data> _member;
DemoClass() : _member(CONSTANT, Data(0))
可以在没有动态分配的情况下做同样的事情,如果你使用原始字符存储的成员(具有足够的大小和对齐方式),并用[=构建元素19=]。这也是 vector 所做的。不过有点复杂:
class DemoClass {
private:
std::aligned_storage_t<sizeof(Data), alignof(Data)> storage[CONSTANT];
public:
DemoClass()
{
std::uninitialized_fill(begin(), end(), Data(0));
}
Data* begin() {
return std::launder(reinterpret_cast<Data*>(std::begin(storage)));
}
Data* end() {
return std::launder(reinterpret_cast<Data*>(std::end(storage)));
}
~DemoClass() {
for(Data& d : *this)
d.~Data();
}
};
您编写示例代码的方式并不简单,因为您在 Data
.
上创建了 2 个构造函数
但是, 如果您将 Data
更改为以下内容(请记住:这只是为了避免混淆调用哪个构造函数 - 您没有t提供完整代码):
class Data {
public:
Data(int number = 0){}
};
现在你可以 brace-initalize 加上一个空括号:
_member{ }
这将确保所有成员都已初始化。
另外,我建议使用 std::array<Data, CONSTANT>
而不是 c 数组。
我找到了您的问题的答案here。因此,在您的情况下,应该这样应用此解决方案:
#include <utility>
#include <array>
#define CONSTANT 2
class Data {
public:
Data(int number){}
};
template<typename T, size_t...Ix, typename... Args>
std::array<T, sizeof...(Ix)> repeat(std::index_sequence<Ix...>, Args &&... args) {
return {{((void)Ix, T(args...))...}};
}
template<typename T, size_t N>
class initialized_array: public std::array<T, N> {
public:
template<typename... Args>
initialized_array(Args &&... args)
: std::array<T, N>(repeat<T>(std::make_index_sequence<N>(), std::forward<Args>(args)...)) {}
};
class DemoClass {
private:
initialized_array<Data, CONSTANT> _member;
public:
DemoClass():
_member(1234)
{
// stuff
}
};
那么你的_member
就是静态分配的固定大小的数组。虽然这种方法有点复杂,所以也许有人可以提供更清洁的解决方案。
我有一个带有成员数组的 class。长度是一个常量,但是这个常量直到编译时才知道(在我的实际代码中,这个常量对于不同的编译目标有不同的定义)。数组的类型是 class,没有默认构造函数。
#define CONSTANT 2
class Data {
public:
Data(int number){}
};
class DemoClass {
private:
Data _member[CONSTANT];
public:
DemoClass():
_member{
Data(0),
Data(0)
}
{
// stuff
}
};
在这个例子中,我可以使用初始化列表设置_member
。但是,如果 COSNTANT
的值发生变化,我必须更改该初始化列表。
理论上,将 DemoClass
更改为具有调用另一个参数为 0
的构造函数的默认构造函数适用于我的情况,因为我将始终调用 Data
0
的构造函数。但是,我无法更改 DemoClass
,因为它在外部库中。
我考虑过的一个解决方案是创建以下 class:
class CustomData : public Data {
public:
CustomData() : Data(0){}
};
这行得通,但似乎有点复杂。有没有更简单的方法来初始化这个数组?
一个简单的解决方案是使用 std::vector
。这显然确实有引入动态分配的缺点:
std::vector<Data> _member;
DemoClass() : _member(CONSTANT, Data(0))
可以在没有动态分配的情况下做同样的事情,如果你使用原始字符存储的成员(具有足够的大小和对齐方式),并用[=构建元素19=]。这也是 vector 所做的。不过有点复杂:
class DemoClass {
private:
std::aligned_storage_t<sizeof(Data), alignof(Data)> storage[CONSTANT];
public:
DemoClass()
{
std::uninitialized_fill(begin(), end(), Data(0));
}
Data* begin() {
return std::launder(reinterpret_cast<Data*>(std::begin(storage)));
}
Data* end() {
return std::launder(reinterpret_cast<Data*>(std::end(storage)));
}
~DemoClass() {
for(Data& d : *this)
d.~Data();
}
};
您编写示例代码的方式并不简单,因为您在 Data
.
但是, 如果您将 Data
更改为以下内容(请记住:这只是为了避免混淆调用哪个构造函数 - 您没有t提供完整代码):
class Data {
public:
Data(int number = 0){}
};
现在你可以 brace-initalize 加上一个空括号:
_member{ }
这将确保所有成员都已初始化。
另外,我建议使用 std::array<Data, CONSTANT>
而不是 c 数组。
我找到了您的问题的答案here。因此,在您的情况下,应该这样应用此解决方案:
#include <utility>
#include <array>
#define CONSTANT 2
class Data {
public:
Data(int number){}
};
template<typename T, size_t...Ix, typename... Args>
std::array<T, sizeof...(Ix)> repeat(std::index_sequence<Ix...>, Args &&... args) {
return {{((void)Ix, T(args...))...}};
}
template<typename T, size_t N>
class initialized_array: public std::array<T, N> {
public:
template<typename... Args>
initialized_array(Args &&... args)
: std::array<T, N>(repeat<T>(std::make_index_sequence<N>(), std::forward<Args>(args)...)) {}
};
class DemoClass {
private:
initialized_array<Data, CONSTANT> _member;
public:
DemoClass():
_member(1234)
{
// stuff
}
};
那么你的_member
就是静态分配的固定大小的数组。虽然这种方法有点复杂,所以也许有人可以提供更清洁的解决方案。