我有一个专门的模板化容器类,其中包含固定大小的数组(大小由模板定义):
template<typename T, std::size_t N>
class container {
protected:
T _arr[N];
std::size_t _size = 0; // does not necessarily equal N
\* other members *\
public:
\* ctors and member functions *\
};
现在我想像 c 数组一样初始化其底层数组:
container c = { 1, 2, 3, 4 };
这行代码应将
_arr
初始化为包含 { 1, 2, 3, 4 }
的整数数组。
同时我也希望能够使用其他构造函数。例如:
container c(size, value); // initializes _arr until size with value
我的理想实现想法是将 (constexpr) 初始值设定项列表的大小传递给模板参数大小的方法。
container(const T (&arr)[N]) : _size(N) {
std::copy(std::begin(arr), std::end(arr), _arr);
}
当直接在构造函数中使用它时,这是有效的:
container c({ 1, 2, 3, 4})
,但在使用复制构造时不起作用:container c = {1, 2, 3, 4}
。在数组周围使用另一组花括号可以解决问题(container c = {{1, 2, 3, 4}}
),但这不是我第一次设想这个类时所想的。
template<typename ...U, typename = std::enable_if<std::conjunction_v<std::is_same<T, U>...>> >
container(U... values) : _size(sizeof...(U)) {
T a[] = {values...};
std::move(std::begin(a), std::end(a), _arr);
}
以及类型推导指南:
template<typename ...T>
container(T...) -> container<std::common_type_t<T...>, sizeof...(T)>;
这是我得到的最接近的结果,但不幸的是它直接搞乱了构造函数的使用(
container c(size, value)
)。
我的理想实现想法是将初始化列表的大小作为模板参数传递(例如使用类型推导指南):
container(std::initializer_list<T> list) : _size(list.size()) {
std::copy(std::begin(list), std::end(list), _arr);
}
template<typename L,
typename T = L::value_type,
typename = std::enable_if<std::is_same_v<L, std::initializer_list<T>>> >
container(L init_list) -> container<T, init_list.size()>;
这显然无法编译(
error: template argument 2 is invalid
)。
有没有办法实现这一点,最好是用 constexpr 方式?
inplace_vector
/ static_vector
。他们深思熟虑并选择不支持 CTAD,正是因为最大容量不容易推论。
但是,如果您的用例是初始大小通常是最大大小,则可以混合使用选项 2 和 3。您无法在编译时获取
initializer_list
参数的大小,但可以通过推导指南不必对应于构造函数。你可以做这样的事情:
template<typename T, std::size_t N>
class container {
// ...
// initializer_list constructor, like in (3)
container(std::initializer_list<T> list) : _size(list.size()) {
std::copy(std::begin(list), std::end(list), _arr);
}
};
// Parameter pack deduction guide, like in (2)
template<typename... T>
container(T...) -> container<std::common_type_t<T...>, sizeof...(T)>;
container c{1, 2, 3};
// container<int, 3> deduced from deduction guide, initializer_list<int> constructor used