3

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

2
  • 1
    does example need to take ownership of the unique_ptr? If not then even without the void** complication it'd make sense to just pass it the raw pointer Commented Apr 29, 2021 at 11:18
  • @AlanBirtles it does not need to take ownership, it's just a matter of memory management. Commented Apr 29, 2021 at 11:20

2 Answers 2

3

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));
Sign up to request clarification or add additional context in comments.

2 Comments

It's a reasonable point, my question stems from the fact that all operations I performed in the object so far, render the expected result, but since that is one of the features of UB, that lead me to ask the question. All the several compilers I tested it in seems to be able to perform the expected operations.
The first (and possibly only) member of a unique_ptr is probably just a pointer so casting it to int** might work but it's definitely undefined behaviour and you're just getting lucky/unlucky that it does
1

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).

6 Comments

It makes sense.
Whether it's correct or not depends on what the function actually expects its parameter to be.
It's the usual, treating the value, simple example: **reinterpret_cast<int**>(ptr) = 30;
If example() expects a pointer to a pointer to an int, then get the int * out of your unique_ptr, store it in a plain int *, then pass a pointer to that variable, casted to a void **. In order to get a useful answer on Stackoverflow, it is necessary to ask a useful question.
No, it's not. There's nothing in the standard that guarantees defined behavior if a pointer to a unique_ptr is interpreted as a pointer to a pointer to a value. I outlined several approaches that are as much within the bounds of defined behavior as possible. I'm confident that one of them, either verbatim, as is, or with some minor adaptations, will work here.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.