Is plotting objects in a char array well-formed

It is almost standard use of textbooks to post new

template<size_t Len, size_t Align>
class aligned_memory
{
public:
    aligned_memory() : data((char*)(((std::uintptr_t)mem + Align - 1) & -Align)) {}
    char* get() const {return data;}
private:
    char mem[Len + Align - 1];
    char* data;
};

template<typename T, size_t N>
class Array
{
public:
    Array() : sz(0) {}
    void push_back(const T& t)
    {
        new (data.get() + sz++ * sizeof(T)) T(t);
    }
    void pop_back()
    {
        ((T*)data.get() + --sz)->~T();
    }

private:
    aligned_memory<N * sizeof(T), alignof(T)> data;
    size_t sz;
};

      

Everything seems to be fine until we look at strict anti-aliasing, there seems to be some kind of conflict over whether it is well formed

Camp ill-formed

The camp is well formed

Everyone agrees that char*

it can always refer to another object, but some point out that it is ill-formed to do it the other way around.

It is clear that ours is char[]

converted to char*

, then added to T*

, with which it is used to call its destructor.

So does the above program follow the strict anti-aliasing rule? Specifically, where does the standard say it is well formed or poorly formed?

EDIT: As background information, this is written for C ++ 0x, before alignas

and std::launder

. Don't ask specifically about the C ++ 0x solution, but this is preferable.

alignof

deceives, but its here, for example, purpose.

+3


source to share


2 answers


Gathered from hints in countless helpful comments, here's my interpretation of what's going on.

TL; DR its well formed ‡ see edit


Quoting ok, I find it more logical from [basic.life]

Properties assigned to objects and references throughout the International Standard apply to a given object or reference only during its lifetime.


An object is said to have non-empty initialization if it is of class or aggregate type and it or one of its subobjects is initialized with a constructor other than the trivial default constructor. [...] The lifetime of a type object T

begins when:

  • the store with the correct alignment and size for the type is T

    obtained, and

  • if the object has a non-memory initialization, its initialization is complete.


The lifetime of an object of type o T

ends when:

  • if T is a class type with a non-trivial destructor, the destructor call begins, or

  • the storage that the object is occupying is freed or reused by an object that is not nested in o

From [basic.lval]

If the program tries to access the stored value of an object using a gl value other than one of the following types, the behavior is undefined

  • dynamic object type,

  • cv-qualified version of the dynamic object type,

  • a type similar to the dynamic type of an object,

  • a type that is a signed or unsigned type corresponding to the dynamic type of the object,

  • a type that is a signed or unsigned type that matches the receipt version of the dynamic object type,

  • an aggregate or union type that includes one of the aforementioned types among its members or non-static data members (including recursively, member or non-static data member of a subaggregation or contained union),

  • a type that is (possibly cv-qualified) the base class type of the dynamic object type,

  • a char

    , unsigned char

    or std​::​byte

    .

Let us deduce that



  • Lifetime char

    in the char[]

    end when another object reuses this space.

  • The lifetime of an object of the type T

    starts when called push_back

    .

  • Since the address ((T*)data.get() + --sz)

    always refers to an object with a type T

    whose life time has started and has not yet ended, it is valid to call ~T()

    with it.

  • During this process, the objects char[]

    and char*

    in aligned_memory

    type aliases T

    , but it's legal. Also, they don't get glvalues, so they could be pointers of any type.

To answer my own question in the comments about whether memory is being used well as storage,

U u;
u->~U();
new (&u) T;
((T*)&u)->~T();
new (&u) U;

      

Following the 4 points above, the answer is yes ‡ see edit if the alignment is no weaker than . U

T

‡ EDIT: I neglected the other paragraph [basic.life]

If after the object's lifetime has expired and before the store that the object is being used or released, a new object is created at the storage location where the original object was loaded, a pointer pointing to the original object, a reference referring to the original object, or the name of the original object will automatically refer to the new object and, once the lifetime of the new object has started, can be used to manage the new object if:

  • the storage for the new object exactly overlays the storage location where the original object was located, and

  • the new object is of the same type as the original object (ignoring the top-level cv qualifiers) and

  • the type of the original object is not constant and, if the class type, does not contain a non-static data member whose type is const-qualified or referenced, and

  • the original object was the most derived object of the type T

    , and the new object is the most derived object of the type T

    (that is, they are not subobjects of the base class).

This means that although the use of an object is well-formed, the means the object receives is not. In particular, post C ++ 17 std::launder

should be called

(std::launder((T*)data.get()) + --sz)->~T();

      

Pre-C ++ 17, a workaround would be to use the pointer obtained from the new placement instead of

T* p = new (data.get() + sz++ * sizeof(T)) T(t);  // store p somewhere

      

† Quoted from n4659 as far as I can see the same is true for n1905

+2


source


Placement-new creates an object at the specified location (C ++ 14 expr.new/1) and ends the lifetime of any other object that occupied the location (basic.life/1.4).



The code ((T*)data.get() + --sz)->~T();

refers to the type object T

where the type object is T

. It is perfectly. It doesn't matter if there was a char array at that location.

0


source







All Articles