背景
我正在使用一个具有以下限制的嵌入式平台:
- 没有堆内存
- 没有 Boost 库
- 支持 C++11
我以前遇到过以下问题:
创建一个类类型 T 的数组,其中 T 没有默认构造函数
直到最近项目才添加了 C++11 支持,之前每次处理这个问题时都是使用 ad-hoc 解决方案。现在可以使用 C++11,所以我想尝试制定一个更通用的解决方案。
解决方案尝试
我复制了 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...);
}
这绝对是朝着正确方向迈出的一步。
Array<T,N>
现在可以编译通过,如果传递的参数与T
的构造函数匹配。我唯一剩下的问题是如何构造一个
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)
返回 SOMETHING 可以初始化数组。
// 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
呢?它完全符合您的要求。 - Guillaume Racicotvector
一起使用。 - M.Mstd::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
不可复制怎么办? - Matt Kstd::vector
的 API。 - Matt Kstd::array
不使用std::initializer_list
,而是使用聚合初始化,这是两个不同的概念。例如,您可以拥有std::array<std::unique_ptr<int>, 2> myArray{std::make_unique<int>(), std::make_unique<int>()}
并且它将正确构建。 - Guillaume Racicotstd::array
使用聚合初始化。在你的例子中,myArray
起作用是因为std::make_unique<int>
的返回类型与std::array<std::unique_ptr<int>,2>
的元素类型匹配。这对std::array<T,N>
有一些限制:T
必须可移动或可复制std::array
可以胜任这项工作。 - Matt K