【发布时间】:2026-02-21 22:05:01
【问题描述】:
背景
我正在使用具有以下限制的嵌入式平台:
- 没有堆
- 没有 Boost 库
- C++11被支持
我过去处理过几次以下问题:
创建一个类类型为 T 的数组,其中 T 没有默认构造函数
该项目最近才添加了对 C++11 的支持,直到现在我每次不得不处理这个问题时都在使用临时解决方案。既然 C++11 可用,我想我会尝试做一个更通用的解决方案。
解决方案尝试
我复制了an example of std::aligned_storage 来为我的数组类型提供框架。结果如下所示:
#include <type_traits>
template<class T, size_t N>
class Array {
// Provide aligned storage for N objects of type T
typename std::aligned_storage<sizeof(T), alignof(T)>::type data[N];
public:
// Build N objects of type T in the aligned storage using default CTORs
Array()
{
for(auto index = 0; index < N; ++index)
new(data + index) T();
}
const T& operator[](size_t pos) const
{
return *reinterpret_cast<const T*>(data + pos);
}
// Other methods consistent with std::array API go here
};
这是一个基本类型 - Array<T,N> 仅在 T 是默认可构造的情况下编译。我对模板参数打包不是很熟悉,但是看了一些例子让我想到了以下几点:
template<typename ...Args>
Array(Args&&... args)
{
for(auto index = 0; index < N; ++index)
new(data + index) T(args...);
}
这绝对是朝着正确方向迈出的一步。如果传递的参数与T 的构造函数匹配,则Array<T,N> 现在编译。
我剩下的唯一问题是构造一个Array<T,N>,其中数组中的不同元素具有不同的构造函数参数。我想我可以把它分成两种情况:
1 - 用户指定参数
这是我对 CTOR 的抨击:
template<typename U>
Array(std::initializer_list<U> initializers)
{
// Need to handle mismatch in size between arg and array
size_t index = 0;
for(auto arg : initializers) {
new(data + index) T(arg);
index++;
}
}
这似乎工作正常,除了需要处理数组和初始化列表之间的维度不匹配之外,但有许多方法可以处理这些并不重要的问题。这是一个例子:
struct Foo {
explicit Foo(int i) {}
};
void bar() {
// foos[0] == Foo(0)
// foos[1] == Foo(1)
// ..etc
Array<Foo,10> foos {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
}
2 - 参数遵循模式
在我之前的示例中,foos 使用递增列表进行初始化,类似于std::iota。理想情况下,我想支持类似下面的东西,其中range(int) 返回可以初始化数组的东西。
// One of these should initialize foos with parameters returned by range(10)
Array<Foo,10> foosA = range(10);
Array<Foo,10> foosB {range(10)};
Array<Foo,10> foosC = {range(10)};
Array<Foo,10> foosD(range(10));
谷歌搜索告诉我std::initializer_list 不是一个“普通”容器,所以我认为没有任何方法可以让range(int) 根据函数参数返回std::initializer_list。
同样,这里有几个选项:
- 在运行时指定的参数(函数返回?)
- 编译时指定的参数(
constexpr函数返回?模板?)
问题
- 到目前为止,此解决方案是否存在任何问题?
- 有没有人建议生成构造函数参数?除了硬编码
std::initializer_list,我想不出运行时或编译时的解决方案,所以欢迎提出任何想法。
【问题讨论】:
-
为什么不使用
std:array?它完全符合您的要求 -
您可以将
vector与自定义分配器一起使用,将存储映射到您为此目的预留的任何存储区域 -
@GuillaumeRacicot
std::array可以处理我提到的第二种初始化类型吗?在我的例子中,我可以去:std::array<Foo,3> = { Foo(0), Foo(1), Foo(2) };不过我不知道如何扩展它。如果我将一个函数返回{ Foo(0), Foo(1), Foo(2) }作为std::initializer_list<Foo>并尝试使用它来初始化std::array,我会收到错误消息。我可以使用std::array而不对所有元素进行硬编码吗?如果Foo不可复制怎么办? -
@M.M 是的,我认为这些方面的东西会起作用。我不太喜欢这个,因为容器应该像一个数组而不是像一个向量。如果容器是具有“永久”(相对于容器的生命周期)元素的固定大小,我宁愿不公开
std::vectorAPI。 -
@Matt
std::array不要使用std::initializer_list,而是聚合初始化,这是不同的东西。例如,您可以拥有std::array<std::unique_ptr<int>, 2> myArray{std::make_unique<int>(), std::make_unique<int>()},它会正确构建。
标签: c++ arrays c++11 memory-management initializer-list