不使用聚合初始化直接初始化不可复制、不可移动的成员

Posted

技术标签:

【中文标题】不使用聚合初始化直接初始化不可复制、不可移动的成员【英文标题】:Directly initializing non-copyable, non-movable members without using aggregate initialization 【发布时间】:2020-05-15 23:59:08 【问题描述】:

背景

假设我正在尝试使用平面数组实现一个固定大小的多维数组:

template <class T, std::size_t... Dims>
struct multi_array 
    static constexpr std::size_t size() noexcept
    
        return (Dims * ... * std::size_t1);
    
    std::array<T, size()> _elems;
;

_elems 成员被公开以启用不可复制、不可移动类型的聚合初始化:(假设 non_movable 具有显式 (int) 构造函数)

multi_array<non_movable, 2, 3> arr 
    non_movable(0), non_movable(1), non_movable(2),
    non_movable(3), non_movable(4), non_movable(5)
;

这得益于 C++17 保证的复制省略——_elems 的相应元素直接从未实现的纯右值初始化,无需移动构造函数。

问题

现在的问题是:在上面的声明中,多维数组被初始化为一个一维数组。我将其称为“平面初始化”,而不是“嵌套初始化”:

multi_array<non_movable, 2, 3> arr 
     non_movable(0), non_movable(1), non_movable(2) ,
     non_movable(3), non_movable(4), non_movable(5) 
; // error: too many initializers for 'multi_array<non_movable, 3, 2>'

我们如何启用嵌套初始化,而无需将用于实现multi_array 的底层容器从一维数组更改为多维数组?

我猜这需要一个自定义构造函数,但我不知道如何通过构造函数“透明地”传递未实现的纯右值。我能想到的就是用它们构造一个参数,然后从参数中移动,这对于不可移动的类型不起作用。

最小的可重现示例

#include <array>
#include <cstddef>

struct non_movable 
    explicit non_movable(int) 
    non_movable(const non_movable&) = delete;
    non_movable(non_movable&&) = delete;
    non_movable& operator=(const non_movable&) = delete;
    non_movable& operator=(non_movable&&) = delete;
    ~non_movable() = default;
;

template <class T, std::size_t... Dims>
struct multi_array 
    static constexpr std::size_t size() noexcept
    
        return (Dims * ... * std::size_t1);
    
    std::array<T, size()> _elems;
;

int main()

    multi_array<non_movable, 3, 2> arr 
        non_movable(0), non_movable(1), non_movable(2),
        non_movable(3), non_movable(4), non_movable(5)
    ;
    // multi_array<non_movable, 3, 2> arr 
    //      non_movable(0), non_movable(1), non_movable(2) ,
    //      non_movable(3), non_movable(4), non_movable(5) 
    // ;
    (void)arr;

(live demo)

【问题讨论】:

【参考方案1】:

我不知道如何通过构造函数“透明地”传递未实现的纯右值。

您想要的通常是不可能的。一旦prvalue作为参数传递给函数,它将初始化一个对象参数,或者它会显示一个绑定到引用参数的临时值。无论哪种方式,它都不再是prvalue。

对于您的特定用例,您可以做的最好的事情是将其保留为一个聚合,或者只是接受您必须将其初始化为“平面”多维数组,或者通过将一个数组存储在一个真正的多维数组中大批。两种选择都有权衡。

【讨论】:

我想我还是等 C++42 中加入这个功能吧……

以上是关于不使用聚合初始化直接初始化不可复制、不可移动的成员的主要内容,如果未能解决你的问题,请参考以下文章

不可复制的对象和值初始化:g++ vs msvc

用指向对象的指针初始化不可复制的第三方基类

必须初始化不可为空的实例字段“taskTitle”

python中为啥说元组不可改变

不可小看的移动广告聚合平台-KeyMob

使类不可复制*和*不可移动