anastaciu
anastaciu

Reputation: 23802

Passing smart pointer to function through non smart pointer argument

Consider the following function:

void example(void **ptr){
    std::cout << *ptr << "\n";                   // pointer value
    std::cout << ptr << "\n";                    // pointer address
    std::cout << **reinterpret_cast<int**>(ptr); // value
}

The function signature cannot be changed.

Is the following code valid, or should I use raw pointers?

int main() 
{
    std::unique_ptr<int> x = std::make_unique<int>(20);
    std::cout << x.get() << "\n";                 // pointer value
    std::cout << &x << "\n";                      // pointer address
    example(reinterpret_cast<void**>(&x));
}

Live sample

Upvotes: 3

Views: 344

Answers (2)

molbdnilo
molbdnilo

Reputation: 66431

It is not valid and has undefined behaviour.
(If it appears to work, it's because your particular implementation happens to store the unique_ptr's underlying pointer as its first member in this particular situation. It is still 100% pure crystal-clear undefined behaviour, though.)

If you can't change the function, you need to pass it something that actually is an int** - &x is a std::unique_ptr<int>*, and no amount of casting can make a valid int** from it.
(I have found that it's often a mistake to think of the smart "pointers" as pointers.)

The way to get a pointer to the underlying object in a smart "pointer" is through get():

int* p = x.get();

and then you can pass a pointer to p (you still need to cast it, though):

example(reinterpret_cast<void**>(&p));

Upvotes: 3

Sam Varshavchik
Sam Varshavchik

Reputation: 118352

Generally only void * have special rights and privileges when it comes to playing funny games with casting. However, given the situation that you have to work with:

example(reinterpret_cast<void**>(&x));

Note that you're passing &x and laundering it with reinterpret_cast. In example() you should do exactly the opposite:

void example(void **ptr)
{
    auto x_ptr = reinterpret_cast<std::unique_ptr<int> *>(ptr);

Now you have a std::unique_ptr<int> * to work with. You can proceed according to your original plans. If you wish, your next step can be:

auto &x=*xptr;

And this x is the same x as in your main, for all practical purposes (and it is).

Upvotes: 1

Related Questions