将参数传递给容器构造函数"array-like"
Passing arguments to "array-like" container constructor
背景
我正在使用一个有以下限制的嵌入式平台:
- 无堆
- 没有Boost库
- 支持C++11
在过去,我曾多次处理过以下问题:
创建一个类类型为T的数组,其中T没有默认的构造函数
该项目最近才添加了对C++11的支持,到目前为止,每当我不得不处理这个问题时,我都会使用特别的解决方案。既然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
};
这是一个基本类型——只有当T
是默认可构造的时,Array<T,N>
才会编译。我对模板参数打包不是很熟悉,但看了一些例子,我得出了以下结论:
template<typename ...Args>
Array(Args&&... args)
{
for(auto index = 0; index < N; ++index)
new(data + index) T(args...);
}
这无疑是朝着正确方向迈出的一步。Array<T,N>
现在编译与T
的构造函数匹配的if传递参数。
我剩下的唯一问题是构造一个Array<T,N>
,其中数组中的不同元素具有不同的构造函数参数。我想我可以把它分成两种情况:
1-用户指定参数
这是我对CTO:的刺
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在元素构造方面的完全不灵活,有利于聚合初始化(以及缺少具有灵活元素构造选项的静态分配容器(。我想出的最好的方法是创建一个类似数组的自定义容器,它接受迭代器来构造它的元素。这是一个完全灵活的解决方案:
- 适用于固定大小和动态大小的容器
- 可以将不同或相同的参数传递给元素构造函数
- 可以调用具有一个或多个(元组分段构造(参数的构造函数,甚至可以调用不同元素的不同构造函数(具有控制反转(
例如:
const size_t SIZE = 10;
std::array<int, SIZE> params;
for (size_t c = 0; c < SIZE; c++) {
params[c] = c;
}
Array<Foo, SIZE> foos(iterator_construct, ¶ms[0]); //iterator_construct is a special tag to call specific constructor
// also, we are able to pass a pointer as iterator, since it has both increment and dereference operators
注意:通过使用自定义迭代器类,您可以完全跳过此处的参数数组分配,该类根据动态位置计算其值。
对于多参数构造函数,它将是:
const size_t SIZE = 10;
std::array<std::tuple<int, float>, SIZE> params; // will call Foo(int, float)
for (size_t c = 0; c < SIZE; c++) {
params[c] = std::make_tuple(c, 1.0f);
}
Array<Foo, SIZE> foos(iterator_construct, piecewise_construct, ¶ms[0]);
具体的实现示例是一段很大的代码,所以如果你想了解除总体想法之外的实现细节,请告诉我——我会更新我的答案。
我会使用工厂lambda。
lambda获取一个指向要构造的位置的指针和一个索引,并负责构造。
这使得复制/移动也很容易写入,这是一个好兆头
template<class T, std::size_t N>
struct my_array {
T* data() { return (T*)&buffer; }
T const* data() const { return (T const*)&buffer; }
// basic random-access container operations:
T* begin() { return data(); }
T const* begin() const { return data(); }
T* end() { return data()+N; }
T const* end() const { return data()+N; }
T& operator[](std::size_t i){ return *(begin()+i); }
T const& operator[](std::size_t i)const{ return *(begin()+i); }
// useful utility:
bool empty() const { return N!=0; }
T& front() { return *begin(); }
T const& front() const { return *begin(); }
T& back() { return *(end()-1); }
T const& back() const { return *(end()-1); }
std::size_t size() const { return N; }
// construct from function object:
template<class Factory,
typename std::enable_if<!std::is_same<std::decay_t<Factory>, my_array>::value, int> =0
>
my_array( Factory&& factory ) {
std::size_t i = 0;
try {
for(; i < N; ++i) {
factory( (void*)(data()+i), i );
}
} catch(...) {
// throw during construction. Unroll creation, and rethrow:
for(std::size_t j = 0; j < i; ++j) {
(data()+i-j-1)->~T();
}
throw;
}
}
// other constructors, in terms of above naturally:
my_array():
my_array( [](void* ptr, std::size_t) {
new(ptr) T();
} )
{}
my_array(my_array&& o):
my_array( [&](void* ptr, std::size_t i) {
new(ptr) T( std::move(o[i]) );
} )
{}
my_array(my_array const& o):
my_array( [&](void* ptr, std::size_t i) {
new(ptr) T( o[i] );
} )
{}
my_array& operator=(my_array&& o) {
for (std::size_t i = 0; i < N; ++i)
(*this)[i] = std::move(o[i]);
return *this;
}
my_array& operator=(my_array const& o) {
for (std::size_t i = 0; i < N; ++i)
(*this)[i] = o[i];
return *this;
}
private:
using storage = typename std::aligned_storage< sizeof(T)*N, alignof(T) >::type;
storage buffer;
};
它定义了my_array()
,但只有当你试图编译它时,它才会被编译
支持初始值设定项列表相对容易。当il不够长或太长时,很难决定该怎么办。我想你可能想要:
template<class Fail>
my_array( std::initializer_list<T> il, Fail&& fail ):
my_array( [&](void* ptr, std::size_t i) {
if (i < il.size()) new(ptr) T(il[i]);
else fail(ptr, i);
} )
{}
这需要你输入一个"失败时该怎么办"。我们可以通过添加以下内容默认抛出:
template<class WhatToThrow>
struct throw_when_called {
template<class...Args>
void operator()(Args&&...)const {
throw WhatToThrow{"when called"};
}
};
struct list_too_short:std::length_error {
list_too_short():std::length_error("list too short") {}
};
template<class Fail=ThrowWhenCalled<list_too_short>>
my_array( std::initializer_list<T> il, Fail&& fail={} ):
my_array( [&](void* ptr, std::size_t i) {
if (i < il.size()) new(ptr) T(il[i]);
else fail(ptr, i);
} )
{}
如果我写对了,就会导致初始化器列表太短,从而导致有意义的抛出消息。在您的平台上,如果没有例外,您可以只使用exit(-1)
。
- "error: no matching function for call to"构造函数错误
- 无法推断类中的类型,构造函数采用 std::array
- C++如何在类构造函数中实例化 std::array?
- 我正在使用dev c ++,但收到错误(C++98'array'必须由构造函数初始化)
- 当 T 不可默认构造时,构造函数初始值设定项列表中 std::array<T,N> 的初始化
- 在构造函数中初始化私有 std::array 成员
- 为什么span的数组和std::array构造函数与其容器构造函数不同
- Picky STD :: String Char Array构造函数
- 简化冗余std :: array初始化,当时没有constexpr构造函数
- 无法创建将 std::array<T, n> 作为类成员的构造函数
- 当您声明"pointer type"形式(函数)参数"like an const array"时,什么是常量?
- 使用字符串文本初始化构造函数中的 std::array<char,x> 成员。海湾合作委员会错误?
- ARRAY[T, SIZE]合适的默认构造函数可用
- 如何为 std::array 的列表初始化构造函数编写包装器?
- 为什么std::array不包含初始化列表构造函数
- 对std::array中的元素调用用户定义的构造函数
- 初始化的Array构造函数做什么?
- 将参数传递给容器构造函数"array-like"
- 在类的构造函数初始值设定项中填充 std::array
- 移动构造函数和 'std::array'