未知("模板化")类数量的初始值设定项列表

Initializer list for an unknown ("templated") amount of classes

本文关键字:列表 未知      更新时间:2023-10-16

如果我有一个类模板,其中包含一个数组,其中另一个类的类型具有未定义的字段数量(数量是模板参数(,我如何运行它们的构造函数(如果它们采用参数(?

这里有一些示例代码:

class ArrayClass
{
public:
ArrayClass() = delete;
ArrayClass(int anyParameter) {}
};
template <const int amountOfFields>
class ContainingClass
{
ArrayClass myArray[amountOfFields];
public:
ContainingClass();
};
template <const int amountOfFields>
ContainingClass<amountOfFields>::ContainingClass()
:
myArray(5)     // doesn't work of cause
{}

是否可以为每个 ArrayClass(无论有多少个(提供相同的参数(或不同的参数(?(我本质上不需要它,但它会让我的事情变得更容易(

在这种情况下,C++标准库中没有任何内容

如果你使用 GCC 进行编译,它有一个称为远程初始化的专有扩展。使用 GCC,您可以编写如下内容(未经测试(:

template<size_t amountOfFields>
ContainingClass<amountOfFields>::ContainingClass():
myArray( { [0 ... (amountOfFields-1)] = 5} )
{ }

如果您使用的是任何其他编译器,则有以下选项。

  1. 正如评论者所说,将数组替换为 std::vector,它具有您需要的构造函数。但是,这将改变RAM布局,即如果你有很多容器,每个容器的元素数量很少,数组(C数组和C++std::array(会更快,因为少了一个要追逐的指针。

  2. 从 ArrayClass 的默认构造函数中删除"=delete",在构造函数中使用std::fillstd::fill_n在构造初始值后设置初始值。但是,这可能会带来一些较小的运行时成本。

  3. 如果你没有太多的元素,从技术上讲,你可以使用一些模板元编程来按照你想要的方式实现静态构造的数组。但是,IMO 将很难调试大量C++代码(没有编译时调试器(。

  4. 如果你的代码中有少量不同的模板参数,你可以编写一个函数,

    template<size_t N>
    constexpr std::array<ArrayClass,N> fill_array(int val)
    

专门针对您拥有的 amountOfFields 寺庙参数的不同值,并在 ContainingClass 的构造函数中调用该函数。

其他解决方案是可能的,如外部工具、宏、提升等......但我认为 2 和 4 是最合理的解决方法。

这适用于GCC 8.1/Clang 6.0和C++14,尽管我绝对不确定它是否符合标准:

class E {
public:
E() = delete;
E(int i) : i_(i) { }
operator int() const { return i_; }
private: 
int i_;
};
template <typename T>
T dummy(T val, /* [[maybe_unused]] */ size_t I) { return val; }
template <typename T, size_t... I, typename U>
constexpr auto make_array_impl(U val, std::index_sequence<I...> is) {
return std::array<T, is.size()>{dummy(val, I)...};
}
template <typename T, size_t N, typename U>
constexpr auto make_array(U val) {
return make_array_impl<T>(val, std::make_index_sequence<N>{});
}
template <typename T, size_t N>
class A {
public: 
A(T val) : a_{make_array<T, N>(val)} { }
void print() { for (auto e : a_) std::cout << e << std::endl; }
private:
std::array<T, N> a_;
};
int main() {
A<E, 5> a(-1);    
a.print();
}

现场演示:https://wandbox.org/permlink/Db9Zpf6gUMvg4MER


更新了更通用的解决方案:

template <typename T, size_t... I, typename... Args>
constexpr auto make_array_impl(std::index_sequence<I...> is, Args&&... args) {
return std::array<T, is.size()>{ (I, T(std::forward<Args>(args)...))... };
}
template <typename T, size_t N, typename... Args>
constexpr auto make_array(Args&&... args) {
return make_array_impl<T>(std::make_index_sequence<N>{}, std::forward<Args>(args)...);
}