构造函数初始值设定项列表中长度未知的数组

Posted

技术标签:

【中文标题】构造函数初始值设定项列表中长度未知的数组【英文标题】:Array of unknown length in constructor initializer list 【发布时间】:2019-07-03 12:51:42 【问题描述】:

我有一个带有成员数组的类。长度是一个常数,但是这个常数直到编译时才知道(在我的实际代码中,这个常数对于不同的编译目标有不同的定义)。数组的类型是一个没有默认构造函数的类。

#define CONSTANT 2

class Data 
public:
    Data(int number)
;

class DemoClass 
private:
    Data _member[CONSTANT];
public:
    DemoClass():
        _member
            Data(0),
            Data(0)
        
    
        // stuff
    
;

在这个例子中,我可以使用初始化列表设置_member。但是,如果 COSNTANT 的值发生变化,我必须更改该初始化列表。

理论上,将DemoClass 更改为具有一个默认构造函数,该构造函数使用0 的参数调用另一个构造函数,这适用于我的情况,因为我将始终使用0 调用Data 构造函数。但是,我无法更改 DemoClass,因为它位于外部库中。

我考虑过的一个解决方案是创建以下类:

class CustomData : public Data 
public:
    CustomData() : Data(0)
;

这可行,但似乎有点复杂。有没有更简单的方法来初始化这个数组?

【问题讨论】:

使用std::vector。它有一个构造函数,您可以为要创建的对象数量提供一个值,以及一个要复制到所有这些对象中的对象。 我正在一个环境中工作,我希望尽量减少动态内存分配的使用。有没有办法在没有动态分配的情况下获得std::vector 的这个功能? 您可以编写/获取堆栈分配器并将其用作向量的分配器,因此它不使用堆。 以这种方式使用初始化列表意味着您要手动初始化每个成员。如果要将所有元素初始化为一个值,请遍历元素并将该值分配给它们。 你确实有Data(),这就是默认构造函数。编辑:无论如何,在你编辑它之前你已经在那里了。 【参考方案1】:

我找到了您问题的答案here。因此,在您的情况下,应该这样应用此解决方案:

#include <utility>
#include <array>

#define CONSTANT 2

class Data 
public:
    Data(int number)
;

template<typename T, size_t...Ix, typename... Args>
std::array<T, sizeof...(Ix)> repeat(std::index_sequence<Ix...>, Args &&... args) 
   return ((void)Ix, T(args...))...;


template<typename T, size_t N>
class initialized_array: public std::array<T, N> 
public:
    template<typename... Args>
    initialized_array(Args &&... args)
        : std::array<T, N>(repeat<T>(std::make_index_sequence<N>(), std::forward<Args>(args)...)) 
;

class DemoClass 
private:
    initialized_array<Data, CONSTANT> _member;
public:
    DemoClass():
        _member(1234)
    
        // stuff
    
;

那么你的_member 是静态分配的固定大小的数组。不过这种方法有点复杂,所以也许有人可以提供一个更清洁的解决方案。

【讨论】:

我接受了这个答案,因为它确实回答了我所写的问题。但是,它实际上只是告诉我应该使用更简单的解决方案,例如找到一种在Data 上实现默认构造函数的方法。看到所有这些答案让我意识到为什么没有简单的单行解决方案来初始化没有默认构造函数的对象数组。【参考方案2】:

一个简单的解决方案是使用std::vector。这显然有引入动态分配的缺点:

std::vector<Data> _member;

DemoClass() : _member(CONSTANT, Data(0))

如果您使用原始字符存储的成员(具有足够的大小和对齐方式)并使用placement-new 构造元素,则可以在没有动态分配的情况下执行相同的操作。这也是矢量的作用。但是有点复杂:

class DemoClass 
private:
    std::aligned_storage_t<sizeof(Data), alignof(Data)> storage[CONSTANT];
public:
    DemoClass()
    
        std::uninitialized_fill(begin(), end(), Data(0));
    

    Data* begin() 
        return std::launder(reinterpret_cast<Data*>(std::begin(storage)));
    

    Data* end() 
        return std::launder(reinterpret_cast<Data*>(std::end(storage)));
    

    ~DemoClass() 
        for(Data& d : *this)
            d.~Data();
    
;

【讨论】:

Data 没有默认构造函数。 @ItsTimmy 哦。您应该相应地更改示例。 哦,我明白了,我在测试时不小心留下了默认构造函数。很抱歉! @ItsTimmy 我重写了答案。【参考方案3】:

您编写示例代码的方式并不简单,因为您在 Data 上创建了 2 个构造函数。

但是, 如果您将 Data 更改为以下内容(请记住:这只是为了避免混淆调用哪个构造函数 - 您没有提供完整代码):

class Data 
public:
    Data(int number = 0)
;

现在你可以用一个空大括号来初始化:

_member 

这将确保所有成员都已初始化。

另外,我建议使用 std::array&lt;Data, CONSTANT&gt; 而不是 c 数组。

【讨论】:

以上是关于构造函数初始值设定项列表中长度未知的数组的主要内容,如果未能解决你的问题,请参考以下文章

为啥在使用大括号初始值设定项列表时首选 std::initializer_list 构造函数?

为啥 C# 3.0 对象初始值设定项构造函数括号是可选的?

React 中是不是仍需要带有自动绑定和属性初始值设定项的构造函数

TypeScript是否支持带有对象初始值设定项的构造函数?

如何从对的初始值设定项列表构造对象?

C++中数组的构造函数初始化列表