std::array是否可以别名较大数组的片段

Can a std::array alias a fragment of a larger array?

本文关键字:数组 片段 array 是否 std 别名      更新时间:2023-10-16

假设我们有一个指针T* ptr;ptr, ptr+1, … ptr+(n-1)都指向T类型的有效对象。

是否可以像访问STL array一样访问它们?或者执行以下代码:

std::array<T,n>* ay = (std::array<T,n>*) ptr

调用未定义的行为?

是的,这是一个未定义的行为,一个经典的行为。。。

首先,了解您刚才所做的:

std::array<T,n>* ay = (std::array<T,n>*) ptr

可以翻译为:

using Arr = std::array<T,n>;
std::array<T,n>* ay = reinterpret_cast<Arr*>( const_cast<TypeOfPtr>(ptr));

您不仅取消了所有constvolatile资格,还取消了类型。请参阅以下答案:https://stackoverflow.com/a/103868/1621391。。。不加区分地丢弃cv资格也可能导致UB。

其次,通过从一个不相关的类型投射的指针访问对象是未定义的行为。请参阅严格的混叠规则(感谢天顶)。因此,通过指针ay的任何读或写访问都是未定义的。如果你非常幸运,代码应该会立即崩溃。如果它奏效了,邪恶的日子就在等着你。。。。

注意,std::array不是也永远不会与任何不是std::array的东西相同。

只是添加。。。在C++标准的工作草案中,它列出了明确的转换规则(你可以阅读它们)并有一个条款说明

5.4.3:以下未提及且未明确定义的任何类型转换用户([class.cov])格式不正确。


我建议你自己做array_view(希望在C++17中出现)。这真的很容易。或者,如果你想要一些所有权,你可以做一个简单的例子:

template<typename T>
class OwnedArray{
    T* data_ = nullptr;
    std::size_t sz = 0;
    OwnedArray(T* ptr, std::size_t len) : data_(ptr), sz(len) {}
public:
    static OwnedArray own_from(T* ptr, std::size_t len)
    { return OwnedArray(ptr, len);  }
    OwnedArray(){}
    OwnedArray(OwnedArray&& o)
    { data_ = o.data_; sz = o.sz; o.data_=nullptr; o.sz=0; }
    OwnedArray& operator = (OwnedArray&& o)
    { delete[] data_; data_ = o.data_; sz = o.sz; o.data_=nullptr; o.sz=0; }
    OwnedArray(const OwnedArray& o) = delete;
    OwnedArray& operator = (const OwnedArray& o) = delete;    
    ~OwnedArray(){ delete[] data_; }
    std::size_t size() const { return sz; }
    T* data() return { data_; }
    T& operator[] (std::size_t idx) { return data_[idx]; }
};

您还可以根据自己的喜好推出更多的成员函数/常量限定。但这有一些警告。。。指针必须已通过new T[len] 分配

因此,您可以在您的示例中使用它,如下所示:

auto ay = OwnedArray<decltype(*ptr)>::own_from(ptr, ptr_len);
是的,这会调用未定义的行为。通常情况下,不能在彼此之间强制转换指向不相关类型的指针。

该代码与没有什么不同

std::string str;
std::array<double,10>* arr = (std::array<double,10>*)(&str);

说明:标准不对std::array<T,n>T*之间的任何兼容性提供任何保证。它根本不存在。也没有说std::array是平凡型。如果没有这样的保证,T*std::array<T,n>之间的任何转换都是未定义的行为,其规模与指向任何不相关类型的指针之间的转换相同。

我也看不出将已经构建的动态数组作为std::array进行访问有什么好处。

p.S.常规免责声明。演员阵容本身总是百分之百的好。触发焰火的是结果指针的间接性,但为了简化起见,省略了这一部分。

我在这里回答第一个问题,因为第二个问题已经在其他答案中处理过了:

回顾:你。。。

具有指针CCD_ 18和CCD_

你问它是否。。。

是否可以像STL array一样访问它们?

答:这没有问题,但它的工作方式与您在代码示例中估计的不同:

std::array<T*, N> arr;
for(int i = 0; i<N; ++i)
{
    arr[i] = ptr + i;
}

现在,您可以像使用原始指针一样使用数组元素。任何地方都没有未定义的行为。