简体   繁体   中英

static_cast of unique_ptr to void *, with constructor arguments

I have a problem in making a static_cast of an unique_ptr void * by passing arguments to the constructor. In particular, what you see in the object3 .

It is important that the solution is at compile time, and the type must be the same for smart pointers as for stacked objects.

Any solution?

class Test {
public:
    Test(){}
    Test(int n) : num(n) {}
    int num;
};

template<typename T>
class Object {
public:
    Object(T&& v) : value(static_cast<T*>(std::move(&v))) {}
    Object(const T& v) : value(static_cast<T*>(&v)) {}

    Object(std::unique_ptr<T>&& v) : value(static_cast<std::unique_ptr<T>*>(std::move(&v))) {}
    Object(const std::unique_ptr<T>& v) : value(static_cast<std::unique_ptr<T>*>(&v)) {}

    T* operator->() { return static_cast<T*>(value); }
private:
    void* value;
};

int main(int argc, char *argv[]) {

    Object<Test> object1 = Test(1);
    cout << object1->num << endl; // print 1

    Object<Test> object2 = Test();
    object2->num = 2;
    cout << object2->num << endl; // print 2

    Object<Test> object3 = std::make_unique<Test>(3);
    cout << object3->num << endl; // print 0 ¿?¿?¿?

    Object<Test> object4 = std::make_unique<Test>();
    object4->num = 4;
    cout << object4->num << endl; // print 4

    return 0;
}

result:

1
2
0
4

Maybe you want:

template<typename T>
class Object {
public:
    Object(const T& v) : value(v) {}
    Object(const std::unique_ptr<T>& v) : value(*v) {}

    T* operator->() { return &value; }
private:
    T value;
};

so you won't have dangling pointer anymore.

Demo

For more pointer semantic-like:

template<typename T>
class Object {
public:
    Object(const T& v) : value(std::make_shared(v)) {}
    Object(std::unique_ptr<T>&& v) : value(std::move(v)) {}

    T* operator->() { return value.get(); }
private:
    std::shared_pointer<T> value;
};

Demo

What you are noticing is undefined behavior.

The lifetime of the unique_ptr is not what you expect.

Object<Test> object3 = std::make_unique<Test>(3);
cout << object3->num << endl; // print 0 ¿?¿?¿?

You make a temporary unique_ptr, after which in the constructor you take the address of that temporary.

Object(std::unique_ptr<T>&& v) : value(static_cast<std::unique_ptr<T>*>(std::move(&v))) {}

In this statement, you take the address of that unique_ptr, after which you move that pointer. For a raw pointer, that has no extra effect.

You can have the same unexpected result for every other of your statements as all of them use temporaries.

Note that you also wrote a reinterpret_cast from the unique_ptr to a raw pointer.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM