Suppose we have a pointer T* ptr;
and ptr, ptr+1, … ptr+(n-1)
all refer to valid objects of type T.
Is it possible to access them as if they were an STL array
? Or does the following code:
std::array<T,n>* ay = (std::array<T,n>*) ptr
invoke undefined behaviour?
An array of references is illegal because a reference is not an object. According to the C++ standard, an object is a region of storage, and it is not specified if a reference needs storage (Standard §11.3. 2/4). Thus, sizeof does not return the size of a reference, but the size of the referred object.
An array is represented by a variable that is associated with the address of its first storage location. A pointer is also the address of a storage location with a defined type, so D permits the use of the array [ ] index notation with both pointer variables and array variables.
An array is a pointer. An array is considered to be the same thing as a pointer to the first item in the array. That rule has several consequences. An array of integers has type int*.
Yes, its an Undefined Behavior, a classic one...
First, understand that what you just did:
std::array<T,n>* ay = (std::array<T,n>*) ptr
can be translated as:
using Arr = std::array<T,n>;
std::array<T,n>* ay = reinterpret_cast<Arr*>( const_cast<TypeOfPtr>(ptr));
You've not just casted away all, const
and volatile
qualification but also casted the type. See this answer: https://stackoverflow.com/a/103868/1621391 ...indiscriminately casting away cv
qualifications can also lead to UB.
Secondly, It is undefined behavior to access an object through a pointer that was casted from an unrelated type. See the strict aliasing rule (Thanks zenith). Therefore any read or write access through the pointer ay
is undefined. If you are extremely lucky, the code should crash instantly. If it works, evil days are awaiting you....
Note that std::array
is not and will never be the same as anything that isn't std::array
.
Just to add... In the working draft of the C++ standard, it lists out explicit conversion rules. (you can read them) and has a clause stating that
.....
5.4.3: Any type conversion not mentioned below and not explicitly defined by the user ([class.conv]) is ill-formed.
.....
I suggest you cook up your own array_view
(hopefully coming in C++17). Its really easy. Or, if you want some ownership, you can cook up a simple one like this:
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]; }
};
...and you can roll out more member functions/const qualifications as you like. But this has caveats... The pointer must have been allocated the through new T[len]
Thus you can use it in your example like this:
auto ay = OwnedArray<decltype(*ptr)>::own_from(ptr, ptr_len);
Yes, this invokes undefined behaviour. Generally you can't cast pointers to unrelated types between each other.
The code is no different from
std::string str;
std::array<double,10>* arr = (std::array<double,10>*)(&str);
Explanation: Standard does not provide any guarantee for any compatibility between std::array<T,n>
and T*
. It is simply not there. It doesn't say that std::array
is trivial type either. Absent such guarantees, any conversion between T*
and std::array<T,n>
is undefined behavior on the same scale as conversion between pointers to any unrelated types.
I also fail to see what is the benefit of accessing already constructed dynamic array as an std::array
.
P.S. Usual disclaimer. Cast, on it's own, is always 100% fine. It is indirection of resulted pointer which triggers the fireworks - but this part is omited for simplicty.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With