-1

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
Jarod42
  • 203,559
  • 14
  • 181
  • 302
Nadalet
  • 57
  • 1
  • 7
  • 2
    why is `value` not a `T*` ? – 463035818_is_not_an_ai May 11 '18 at 14:02
  • If value was T, the type should be different: Object > for smart pointers, and Object for stacked objects. What interests me is that the type is Object for both. – Nadalet May 11 '18 at 14:05
  • 3
    I'm entirely unclear what you expect from `std::move(&v)` – François Andrieux May 11 '18 at 14:07
  • Your code require `const_cast` to remove `const`. – Jarod42 May 11 '18 at 14:08
  • 1
    `cout << object1->num << endl;` is UB, as you dereference a dangling pointer. (as all your print in fact) – Jarod42 May 11 '18 at 14:09
  • 3
    sorry I dont understand your comment. I am asking, because your `operator->` returns a `T*` anyhow, so I dont see the point of having a `void*` member – 463035818_is_not_an_ai May 11 '18 at 14:09
  • @Nadalet you can just add a constructor from `unique_ptr &&p`. – Dan M. May 11 '18 at 14:11
  • 3
    why all those casts? Do you really need to cast a `std::unique_ptr*` to a `std::unique_ptr*` just to store it in a `void*` (...and then cast it to a `T*`) ? – 463035818_is_not_an_ai May 11 '18 at 14:12
  • 1
    For `object3` the member `value` points to a `std::unique_ptr` but you `static_cast` to `T*`. A `std::unique_ptr` is very different from a `T`. Maybe you want a [`std::variant`](http://en.cppreference.com/w/cpp/utility/variant)? – François Andrieux May 11 '18 at 14:15
  • 1
    likely a [xy problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa). It is hard to understand what you are doing there because this is probably an attempt to solve a problem that we dont know. What are you actually trying to achieve here? – 463035818_is_not_an_ai May 11 '18 at 14:17
  • I need that the stacked objects and the pointers have the same syntax to make me easily transpile from another language to c ++. That's why the operator ->. But that is irrelevant to the problem in question. – Nadalet May 11 '18 at 14:23
  • Seems to me like this must be UB. You take a copy of an address that was at some point an LValue with `std::make_unique(3);` After the call the lvalue and wherever it has been moved will disappear and you have a dangling pointer. – Fantastic Mr Fox May 11 '18 at 14:27
  • 1
    not irrelevant, because your current attempt has severe problems and instead of fixing a misled attempt to solve your actual problem we could help you to find a solution to your actual problem – 463035818_is_not_an_ai May 11 '18 at 14:28
  • What is `std::move(&v)`? – curiousguy May 12 '18 at 01:10

2 Answers2

3

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

Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • In your class, creating a pointer, in different scope, does not change the value. Instead, my class, if it is modified. For example, in your class: [Demo](https://ideone.com/BP7CnW) In my class: [Demo](https://ideone.com/cWrME1) It would be good to keep this capacity. – Nadalet May 11 '18 at 15:05
  • Thank you for the reply. But I want to try to use only unique_ptr for performance issues. In my example you can assign the pointer to a different scope and it works. The only thing that does not work is the constructor with arguments doing the make_unique. If there was any way to fix this, it would be perfect. – Nadalet May 11 '18 at 15:19
  • 1
    In your case, you have undefined behavior as you access dangling pointer. You might use `T*` member if you guaranty the lifetime of your object but you don't have it in your usage. – Jarod42 May 11 '18 at 15:22
  • 1
    By modifying your code to make it more secure, you might compare https://ideone.com/IFK1qd and https://ideone.com/usUsa3. – Jarod42 May 11 '18 at 15:27
0

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.

JVApen
  • 11,008
  • 5
  • 31
  • 67