template <class T, size_t Capacity>
class fixed_vector {
using storage_t = std::aligned_storage_t<sizeof(T), alignof(T)>;
size_t length_;
storage_t data_[Capacity];
public:
// ...
T* begin() { return reinterpret_cast<T*>(&data_[0]); }
T* end() { return begin() + length_; } // <== (a)
T* end() { return reinterpret_cast<T*>(&data_[length]); } // <== (b)
// ...
};
T* data() { return reinterpret_cast<T*>(&data_[0]); }
--
---
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-discussion+unsubscribe@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/.
On Thursday, January 12, 2017 at 3:08:02 PM UTC-8, Chris Hallock wrote:This is issue 2182.
Does GCC do anything different with any of these changes?
- data_ is declared as aligned_storage_t<Capacity*sizeof(T), alignof(T)> data_;
- data_ is declared as alignas(T) unsigned char data_[Capacity*sizeof(T)];
- std::launder is used on the result of reinterpret_cast
Shouldn't a container call placement new, such that a T exists? If so, what's the point of std::launder?
On 13 January 2017 at 23:07, Nicol Bolas <jmck...@gmail.com> wrote:
>
>
> On Friday, January 13, 2017 at 3:28:24 PM UTC-5, Myriachan wrote:
>>
>> On Thursday, January 12, 2017 at 3:08:02 PM UTC-8, Chris Hallock wrote:
>>>
>>> This is issue 2182.
>>>
>>> Does GCC do anything different with any of these changes?
>>>
>>> data_ is declared as aligned_storage_t<Capacity*sizeof(T), alignof(T)>
>>> data_;
>>> data_ is declared as alignas(T) unsigned char data_[Capacity*sizeof(T)];
>>> std::launder is used on the result of reinterpret_cast
>>
>> Shouldn't a container call placement new, such that a T exists? If so,
>> what's the point of std::launder?
>
>
> Just because there's a different object there doesn't mean you're allowed to
> access it through the original pointer/variable. You're only allowed to do
> that if you didn't change the type of the object. Which you did, in this
> case.
>
> So `launder` is needed if you have the old pointer/variable and want to
> convert it to the object that exists in its place.
The point of launder was to provide a mechanism for telling optimizers
not to assume
that a memory location where there was a reference or a const member
can not change.
std::aligned_union_t<SomeType> val;
new(&val) SomeType();
SomeType *p = <<Convert From val to SomeType*>>
#include <cstddef>
struct Meow
{
int x[14];
float y;
int z;
};
int Kitty(Meow *meow)
{
meow->z = 4;
char *temp = reinterpret_cast<char *>(meow->x);
temp += offsetof(Meow, z) - offsetof(Meow, x);
*reinterpret_cast<int *>(temp) = 2;
return meow->z;
}
--
On Friday, January 13, 2017 at 1:56:28 PM UTC-8, Nicol Bolas wrote:On Friday, January 13, 2017 at 4:15:16 PM UTC-5, Ville Voutilainen wrote:
The point of launder was to provide a mechanism for telling optimizers
not to assume
that a memory location where there was a reference or a const member
can not change.
That may be true, but the current wording around launder makes it clear that it is the only valid way to do the conversion below:
std::aligned_union_t<SomeType> val;
new(&val) SomeType();
SomeType *p = <<Convert From val to SomeType*>>
[basic.life]/8 doesn't allow `reinterpret_cast` alone to work here, regardless of the members of `SomeType`. Only `std::launder` (as noted in the non-normative notation) does.
I think the main thing we need to make `vector` work is the following:
> If I have enough storage for X copies of `SomeType`, and I construct X of them, with `sizeof(SomeType)` bytes between each, then that storage counts as an array of X `SomeType`s for pointer arithmetic purposes.
That's the main problem we have. Pointer arithmetic is defined based on having arrays, and I know of no language in the specification that allows you to treat two instances created sequentially in the same storage as an array.
The rule gets complicated if you don't simply allow all pointer arithmetic so long as it never leaves the boundaries of a single allocated storage block, and as long as you don't dereference it unless the created type matches (for aliasing rules).
We could take a similar approach as for single object pointer arithmetic. I.e. every contiguous sequence of objects of the same (most-derived) type is, for the purpose of pointer arithmetic, a hypothetical array?