std :: array是否可以为更大数组的片段添加别名?

时间:2016-04-08 20:42:33

标签: c++ arrays c++11 strict-aliasing type-punning

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

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

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

调用未定义的行为?

3 个答案:

答案 0 :(得分:3)

是的,它是一个未定义的行为,一个经典的行为......

首先,要了解你刚刚做了什么:

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。

其次,通过从不相关类型转换的指针访问对象是未定义的行为。见the strict aliasing rule(谢谢天顶)。因此,通过指针ay的任何读或写访问都是未定义的。如果你非常幸运,代码应该立即崩溃。如果它奏效了,邪恶的日子正等着你......

请注意,std::array不会,也不会与非std::array的任何内容相同。

只需添加...在working draft of the C++ standard中,它会列出out explicit conversion rules(你可以阅读)并有一个陈述

的条款
  

.....

     

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

     

.....

我建议你自己做饭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]; }
};

...您可以根据需要推出更多成员函数/ const资格。但这有警告......指针必须已经通过new T[len]

分配

因此,你可以在你的例子中使用它:

auto ay = OwnedArray<decltype(*ptr)>::own_from(ptr, ptr_len);

答案 1 :(得分:2)

是的,这会调用未定义的行为。通常,您无法在彼此之间转换指向不相关类型的指针。

代码与

没有什么不同
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。通常的免责声明。对它自己来说,总是100%罚款。它是导致烟花爆发的结果指针的间接 - 但这部分省略了简单。

答案 2 :(得分:0)

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

回顾:你......

  

指针T* ptr;ptr, ptr+1, … ptr+(n-1)都引用T类型的有效对象。

你问是不是......

  

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

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

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

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