Search code examples
c++standards

new (ptr) T() == static_cast<T*>(ptr)?


I want to implement something like rust's dyn trait(I know this doesn't work for multiple inheritance)

template<template<typename> typename Trait>
class Dyn
{
    struct _SizeCaler:Trait<void>{ void* _p;};
    char _buffer[sizeof(_SizeCaler)];
public:
    template<typename T>
    Dyn(T* value){
        static_assert(std::is_base_of_v<Trait<void>,Trait<T>>
            ,"error Trait T,is not derive from trait<void>"
        );

        static_assert(sizeof(_buffer) >= sizeof(Trait<T>)
            ,"different vtable imple cause insufficient cache"
        );

        new (_buffer)Trait<T>{value}; 
    }
    Trait<void>* operator->(){
        return static_cast<Trait<void>*>(reinterpret_cast<void*>(_buffer));
    }
};

template<template<typename> typename Trait,typename T>
struct DynBase:Trait<void>
{
protected:
    T* self;
public:
    DynBase(T* value):self(value){}
};

struct Point{
    double x,y;
};

struct Rect{
    Point m_leftDown,m_rightUp;
    Rect(Point p1,Point p2){
        m_leftDown = Point{std::min(p1.x,p2.x),std::min(p1.y,p2.y)}; 
        m_rightUp = Point{std::max(p1.x,p2.x),std::max(p1.y,p2.y)}; 
    }
};

template<typename = void>
struct Shape;

template<>
struct Shape<void>
{
    virtual double area() = 0;
};

template<>
struct Shape<Rect> final
    :DynBase<Shape,Rect>
{
    using DynBase<Shape,Rect>::DynBase;
    double area() override{
        return (self->m_rightUp.x - self->m_leftDown.x )
            * (self->m_rightUp.y - self->m_leftDown.y);
    }
};

void printShape(Dyn<Shape> ptr)
{
    std::cout << ptr->area();
}

int main()
{
    Rect r{{1,2},{3,4}};
    printShape(&r);
}

but I found that the c++standard may not be able to derive “new (ptr) T() == static_cast<T*>(ptr)“? So conversely, “static_cast<T*>(ptr) == new (ptr) T()” cannot prove

Trait<void>* operator->(){ return static_cast<Trait<void>*>(reinterpret_cast<void*>(_buffer)); }

Other attempts

a abstruct class can't be a union member(why?),and I can't use placement new to calculate offset at compile time, because Trait is an abstract class.

So does the standard specify the validity of static_cast<T*>(ptr) == new (ptr) T()?


Solution

  • The expressions new (ptr) T() and static_cast<T*>(ptr), where ptr has type void*, will return the same address, ptr (as long as T is a scalar type -- array types are allowed to have overhead when dynamically allocated)

    However, the semantics are quite different.

    • In new (ptr) T(), a new object of type T is created at the specified address. Any prior value is lost.

    • In static_cast<T*>(ptr), there must already be an object of type T at the specified address, or else you are setting yourself up to violate the strict aliasing rule.


    If ptr had a type other than void*, it would need to be related to T by inheritance or trivial adjustments (such as changing signedness or adding const or volatile), and the address might have an offset applied as a result of multiple inheritance. But that will never happen in the code of this question.