如何在没有默认构造函数的情况下初始化 class 的 std::array?
How can I initialize an std::array of a class without a default constructor?
假设我有一个 class 没有名为 Foo
的默认构造函数。
如果我使用 std::vector
,我可以这样做:
std::vector<Foo> vec(100, Foo(5));
这将创建一个包含 100 个元素的向量,每个元素的值为 Foo(5)
。
如何对 std::array<Foo, 100>
执行相同的操作?
我显然不想在初始化列表中显式列出 Foo(5)
100 次。但是我不能等到数组构造完成后再初始化它,因为缺少默认构造函数会产生编译错误。
通过提供类似于“placement new”或 emplace
函数的显式构造函数参数,让我也可以避免复制构造函数的解决方案加分。
std::array
只是一个固定数组的薄包装。它没有 repeat-insert 逻辑,就像 std::vector
那样。由于 Foo
没有默认构造函数,初始化 std::array<Foo, N>
实例的唯一方法是使用 aggregate initialization 并指定 N
个值(抱歉,我知道你不想这样做),例如:
std::array<Foo, 100> arr{5, 5, 5, ...}; // N times...
否则,您将不得不创建一个足够大小的字节数组,然后在循环中使用placement-new
,例如:
std::aligned_storage_t<sizeof(Foo), alignof(Foo)> arr[100];
for(int i = 0; i < 100; ++i) {
new (&arr[i]) Foo(5);
}
...
// use static_cast<Foo*>(&arr[index]) to access each object as needed...
...
for(int i = 0; i < 100; ++i) {
// when using placement-new, you must call each object's destructor explicitly...
static_cast<Foo*>(&arr[i])->~Foo();
}
使用复制构造函数,大致如下:
template <typename T, size_t... Is>
std::array<T, sizeof...(Is)> MakeArrayHelper(
const T& val, std::index_sequence<Is...>) {
return {(static_cast<void>(Is), val) ...};
}
template <typename T, size_t N>
std::array<T, N> MakeArray(const T& val) {
return MakeArrayHelper<T>(val, std::make_index_sequence<N>{});
}
std::array<Foo, 100> arr = MakeArray<Foo, 100>(Foo(5));
其实不用拷贝构造函数也可以做到。该解决方案严重依赖于 C++17 的强制复制省略。
template <typename T, size_t... Is, typename... Args>
std::array<T, sizeof...(Is)> MakeArrayHelper(
std::index_sequence<Is...>, Args&&... args) {
return {(static_cast<void>(Is), T{std::forward<Args>(args)...}) ...};
}
template <typename T, size_t N, typename... Args>
std::array<T, N> MakeArray(Args&&... args) {
return MakeArrayHelper<T>(std::make_index_sequence<N>{},
std::forward<Args>(args)...);
}
假设我有一个 class 没有名为 Foo
的默认构造函数。
如果我使用 std::vector
,我可以这样做:
std::vector<Foo> vec(100, Foo(5));
这将创建一个包含 100 个元素的向量,每个元素的值为 Foo(5)
。
如何对 std::array<Foo, 100>
执行相同的操作?
我显然不想在初始化列表中显式列出 Foo(5)
100 次。但是我不能等到数组构造完成后再初始化它,因为缺少默认构造函数会产生编译错误。
通过提供类似于“placement new”或 emplace
函数的显式构造函数参数,让我也可以避免复制构造函数的解决方案加分。
std::array
只是一个固定数组的薄包装。它没有 repeat-insert 逻辑,就像 std::vector
那样。由于 Foo
没有默认构造函数,初始化 std::array<Foo, N>
实例的唯一方法是使用 aggregate initialization 并指定 N
个值(抱歉,我知道你不想这样做),例如:
std::array<Foo, 100> arr{5, 5, 5, ...}; // N times...
否则,您将不得不创建一个足够大小的字节数组,然后在循环中使用placement-new
,例如:
std::aligned_storage_t<sizeof(Foo), alignof(Foo)> arr[100];
for(int i = 0; i < 100; ++i) {
new (&arr[i]) Foo(5);
}
...
// use static_cast<Foo*>(&arr[index]) to access each object as needed...
...
for(int i = 0; i < 100; ++i) {
// when using placement-new, you must call each object's destructor explicitly...
static_cast<Foo*>(&arr[i])->~Foo();
}
使用复制构造函数,大致如下:
template <typename T, size_t... Is>
std::array<T, sizeof...(Is)> MakeArrayHelper(
const T& val, std::index_sequence<Is...>) {
return {(static_cast<void>(Is), val) ...};
}
template <typename T, size_t N>
std::array<T, N> MakeArray(const T& val) {
return MakeArrayHelper<T>(val, std::make_index_sequence<N>{});
}
std::array<Foo, 100> arr = MakeArray<Foo, 100>(Foo(5));
其实不用拷贝构造函数也可以做到。该解决方案严重依赖于 C++17 的强制复制省略。
template <typename T, size_t... Is, typename... Args>
std::array<T, sizeof...(Is)> MakeArrayHelper(
std::index_sequence<Is...>, Args&&... args) {
return {(static_cast<void>(Is), T{std::forward<Args>(args)...}) ...};
}
template <typename T, size_t N, typename... Args>
std::array<T, N> MakeArray(Args&&... args) {
return MakeArrayHelper<T>(std::make_index_sequence<N>{},
std::forward<Args>(args)...);
}