隐藏空基类以进行聚合初始化

Posted

技术标签:

【中文标题】隐藏空基类以进行聚合初始化【英文标题】:Hide empty base class for aggregate initialization 【发布时间】:2020-04-17 07:06:11 【问题描述】:

考虑以下代码:

struct A

    // No data members
    //...
;

template<typename T, size_t N>
struct B : A

    T data[N];

这是你必须初始化 B 的方式:B&lt;int, 3&gt; b = , 1, 2, 3 ; 我想避免基类不必要的空 。 Jarod42 here 提出了一个解决方案,但是,它不适用于元素默认初始化:B&lt;int, 3&gt; b = 1, 2, 3; 很好,但 B&lt;int, 3&gt; b = 1; 不是:b.data[1]b.data[2] 没有默认初始化为 0,并发生编译器错误。 有什么方法(或者c++20会有)从构造中“隐藏”基类?

【问题讨论】:

为什么不添加构造函数template&lt;class... Ts&gt; B(Ts... args) : dataargs... 为什么是评论?它似乎正在工作,大声笑 这是一个显而易见的解决方案,我认为您有理由不使用它。 :) 这太容易了xD。如果你把它写成答案,我会接受它 【参考方案1】:

从 C++20 开始,您可以在 aggregate initialization 中使用 designated initializers。

B<int, 3> b =  .data 1 ; // initialize b.data with 1, 
                             // b.data[0] is 1, b.data[1] and b.data[2] would be 0

【讨论】:

这对我来说仍然太冗长,这是一个最小的例子。我的数组成员有一个奇怪的名称,用户应该忽略它【参考方案2】:

仍然使用构造函数,您可能会执行以下操作:

template<typename T, size_t N>
struct B : A

public:
    constexpr B() : data 

    template <typename ... Ts,
              std::enable_if_t<(sizeof...(Ts) != 0 && sizeof...(Ts) < N)
                               || !std::is_same_v<B, std::decay_t<T>>, int> = 0>
    constexpr B(T&& arg, Ts&&... args) : datastd::forward<T>(arg), std::forward<Ts>(args)...
    

    T data[N];
;

Demo

SFINAE 主要是为了避免创建伪拷贝构造函数B(B&amp;)

你需要额外的私有标签来支持B&lt;std::index_sequence&lt;0, 1&gt;, 42&gt; ;-)

【讨论】:

为什么需要((void)Is, T())...?如果你只是省略它怎么办?其余元素不会默认使用T()进行值初始化吗? @Evg:确实,简化了。害怕只默认初始化剩余元素而不是值初始化它们......【参考方案3】:

最简单的解决方案是添加可变参数构造函数:

struct A  ;

template<typename T, std::size_t N>
struct B : A 
    template<class... Ts, typename = std::enable_if_t<
        (std::is_convertible_v<Ts, T> && ...)>>
    B(Ts&&... args) : datastd::forward<Ts>(args)... 

    T data[N];
;

void foo() 
    B<int, 3> b1 = 1, 2, 3;
    B<int, 3> b2 = 1;

如果您在... 初始化器列表中提供的元素少于N,则数组data 中的剩余元素将按T() 进行值初始化。

【讨论】:

我刚刚发现了为什么这与聚合初始化不同。如果您考虑 B&lt;Class, 5&gt; b = Class(); Class 将首先构造然后移动,而通过使用聚合初始化 Class 将在适当位置构造,不涉及移动 @user7769147,说得好。您可以使用std::tuple 的参数并使用它们就地构造对象。但是语法会比较麻烦。 我随机找到了一个可以解决这个问题的解决方案,我将把它作为已接受的答案,以感谢您的参与:)。【参考方案4】:

我找到了另一个解决方案(我不知道如何)完美地解决了我们在 Evg 的回答下讨论的问题

struct A ;

template<typename T, size_t N>
struct B_data

    T data[N];
;

template<typename T, size_t N>
struct B : B_data<T, N>, A

    // ...
;

【讨论】:

有趣的解决方案。但是现在必须使用this-&gt;datausing B_data::data; 才能访问B 内部的data

以上是关于隐藏空基类以进行聚合初始化的主要内容,如果未能解决你的问题,请参考以下文章

C++ EBO 空基类最优化

123

15. 继承重载覆盖隐藏

聚合对象应使用“...”进行初始化

如何从现有的基类对象创建派生类对象?

C++虚基类初始化