Does P0137 make some previously well-defined programs undefined?

235 views
Skip to first unread message

xsk...@gmail.com

unread,
Dec 13, 2017, 12:50:23 AM12/13/17
to ISO C++ Standard - Discussion
For example, consider the example from cppreference.com:
#include <iostream>
#include <type_traits>
#include <string>
 
template<class T, std::size_t N>
class static_vector
{
   
// properly aligned uninitialized storage for N T's
   
typename std::aligned_storage<sizeof(T), alignof(T)>::type data[N];
   
std::size_t m_size = 0;
 
public:
   
// Create an object in aligned storage
   
template<typename ...Args> void emplace_back(Args&&... args)
   
{
       
// ...
   
}
 
   
// Access an object in aligned storage
   
const T& operator[](std::size_t pos) const
   
{
       
return *reinterpret_cast<const T*>(data+pos);
   
}
 
   
// Delete objects from aligned storage
   
~static_vector()
   
{
       
// ...
   
}
};
 
int main()
{
    static_vector
<std::string, 10> v1;
    v1
.emplace_back(5, '*');
    v1
.emplace_back(10, '*');
   
std::cout << v1[0] << '\n' << v1[1] << '\n';
}
 
Before P0137, according to [basic.compound] paragraph 3:

If an object of type T is located at an address A, a pointer of type cv T* whose value is the address A is said to point to that object, regardless of how the value was obtained.

The expression 

reinterpret_cast<const T*>(data+pos)

points to the previously created object of type T, so the result is well-defined. 

However, after P0137, this expression still points to an element of the array `data`, which is of type std::aligned_storage::type. So accessing the value of previously created object of type T through this pointer results in undefined behavior.

Is there something wrong with my comprehension above?

Vicente J. Botet Escriba

unread,
Dec 13, 2017, 2:03:38 AM12/13/17
to std-dis...@isocpp.org, xsk...@gmail.com
Hi,

I suspect the thing is

"an array of unsigned char can be used to hold other objects"


and that
std::aligned_storage<sizeof(T), alignof(T)>

"Provides the nested type type, which is a trivial type suitable for use as uninitialized storage for any object whose size is at most Len and whose alignment requirement is a divisor of Align. "


Vicente

xsk...@gmail.com

unread,
Dec 13, 2017, 2:22:11 AM12/13/17
to ISO C++ Standard - Discussion
But I cannot find words in the current draft saying that a pointer to an object o can be converted to a pointer to another object nested within o. In fact, I don't think they are pointer-interconvertible according to [basic.compound] paragraph 4.

在 2017年12月13日星期三 UTC+8下午3:03:38,Vicente J. Botet Escriba写道:

Vicente J. Botet Escriba

unread,
Dec 13, 2017, 2:35:57 AM12/13/17
to std-dis...@isocpp.org, xsk...@gmail.com
Le 13/12/2017 à 08:22, xsk...@gmail.com a écrit :
But I cannot find words in the current draft saying that a pointer to an object o can be converted to a pointer to another object nested within o. In fact, I don't think they are pointer-interconvertible according to [basic.compound] paragraph 4.
data+pos type is

    std::aligned_storage<sizeof(T), alignof(T)>::type*

If this type isn't already an array of unsigned char, we could have an issue, yes.

So the question is if could be the type of

    std::aligned_storage<sizeof(T), alignof(T)>::type

be something else than an array of unsigned char?
I don't think so.


Vicente



--

---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussio...@isocpp.org.
To post to this group, send email to std-dis...@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.


xsk...@gmail.com

unread,
Dec 13, 2017, 4:44:29 AM12/13/17
to ISO C++ Standard - Discussion
I think it is also undefined even if std::aligned_storage<sizeof(T), alignof(T)>::type is an array of unsigned char (I have stated the reasons in the first post). In addition, this question from stackoverflow may be an evidence.

在 2017年12月13日星期三 UTC+8下午3:35:57,Vicente J. Botet Escriba写道:

Chris Hallock

unread,
Dec 13, 2017, 2:50:44 PM12/13/17
to ISO C++ Standard - Discussion, xsk...@gmail.com
You're right, it's undefined after P0137. You now have to std::launder the result of the reinterpret_cast. Which is too bad. reinterpret_cast ought to be sufficient by itself. If compilers ever start differentiating between std::launder(reinterpret_cast<T*>(ptr)) and reinterpret_cast<T*>(ptr) for optimizations, I will be amazed and confused.

xsk...@gmail.com

unread,
Dec 13, 2017, 10:41:44 PM12/13/17
to ISO C++ Standard - Discussion, xsk...@gmail.com
I agree with you. I think std::launder is raised for the case where a new object is created after the cast. The case where a new object is created before the cast ought to work fine as before, even without std::launder.

在 2017年12月14日星期四 UTC+8上午3:50:44,Chris Hallock写道:
Reply all
Reply to author
Forward
0 new messages