通过将数组本身作为引用参数来初始化数组是否合法?

问题描述 投票:0回答:1

在问题 Idiom for Initializing an std::array using a Generator Function Take the Index?,它基本上询问如何初始化任意类型的数组,而该数组不一定是默认可构造的,我想出了以下(非常非正统的)解决方案。

#include <cstddef>
#include <utility>
#include <array>
#include <iostream>

struct Int{
    int v;
    Int(int v):v{v}{}
};

int main()
{
    auto gen = [](size_t i) { return  Int(11*(i+1)); };
    std::array<Int, 500000> arr = [&arr, &gen](){
        for(std::size_t i=0; i < arr.size(); i++)
            new (arr.data() + i) Int(gen(i));
        return arr;
    }();
    for(auto i : arr) { std::cout << i.v << ' ';}
    std::cout << '\n';
}

在解决方案中,使用函子(在本例中是 lambda,但我对一般情况感兴趣)来初始化数组。函子通过引用初始化数组,通过放置

new
构造非默认可构造元素,然后返回数组。

我不完全确定这是否真的合法。 GCC、Clang 和 MSVC 似乎都表明这是有效的。对于 GCC 和 Clang,我还打开了消毒器,以便可以检测到未定义的行为。

arr.size()
的访问似乎很好,因为它只是一个编译时常量。使用
arr.data()
似乎也很好,因为数组
arr
的生命周期在
=
中的
std::array<int, 500000> arr=
之后开始,并且
arr
有一个明确定义的地址,这应该正是
arr.data()
返回的地址,因为
 arr
是一个聚合,但我不完全确定。我也不确定这个位置
new
从标准角度来看是否有效。对于
arr = [&arr,&gen]{...; return arr;}
,我也不确定 C++17 中的新右值语义是否是使代码片段有效所必需的,或者它在早期 C++ 标准(例如 C++14)中是否也是合法的。

所以问题是,在用于初始化自身的函子中以这种方式访问要初始化的数组是否合法,为什么?


对于其他背景,我已经阅读了激发这个问题的问题的答案(上面链接,现在作为重复项关闭)以及那里的重复项。那里的答案基本上可以归结为两件事之一:

  • 在 lambda 中生成一个本地数组并返回它。这在由于
    const
    导致初始化困难的情况下很有帮助,但在类型不可默认构造的情况下则没有帮助。
  • 使用某种基于
    std::index_sequence
    的变体。这受到模板的实现限制,并且不适用于大数组

基于这些,我相信这里的解决方案有其实用价值,除非有人能想出一个不受上述两个限制的解决方案,而这不仅仅是一个理论兴趣的问题。

c++ c++17 c++14 stdarray object-construction
1个回答
0
投票

使用

arr.data()
似乎也不错,因为数组
arr
的生命周期是在
=
 中的 
std::array<int, 500000> arr=

之后开始的

不,生命周期从初始化完成时开始。

=
之后,名称仅变得可见。

这使得

arr.data()
UB,然后复制
arr
作为回报也是 UB (并且浪费)。

arr
中复制
return
将使
arr
中的原始元素保持活动状态,然后这些元素将被
std::array
的复制构造函数覆盖,而不调用其析构函数,这是另一个问题。

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.