Returning Unique_Ptr from Functions

Returning unique_ptr from functions

is there some other clause in the language specification that this exploits?

Yes, see 12.8 §34 and §35:

When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object [...]
This elision of copy/move operations, called copy elision, is permitted [...]
in a return statement in a function with a class return type, when the expression is the name of
a non-volatile automatic object
with the same cv-unqualified type as the function return type [...]

When the criteria for elision of a copy operation are met and the object to be copied is designated by an lvalue,
overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue.


Just wanted to add one more point that returning by value should be the default choice here because a named value in the return statement in the worst case, i.e. without elisions in C++11, C++14 and C++17 is treated as an rvalue. So for example the following function compiles with the -fno-elide-constructors flag

std::unique_ptr<int> get_unique() {
auto ptr = std::unique_ptr<int>{new int{2}}; // <- 1
return ptr; // <- 2, moved into the to be returned unique_ptr
}

...

auto int_uptr = get_unique(); // <- 3

With the flag set on compilation there are two moves (1 and 2) happening in this function and then one move later on (3).

Return unique_ptr by value or by reference?

Your way of returning std::unique_ptr is correct. It'll either be placed in ptr1 directly via NRVO (Named Return Value Optimization) or moved into it. You should NOT return std::move(wStr_ptr), however, since that will prevent (N)RVO. And it works the same way for std::shared_ptr.

Returning unique_ptr from a function executed via dlsym

Yes, ish, with a lot of caveats. Firstly, using boost or STL within a DSO interface is a little dangerous.

  1. std::unique_ptr differs between compilers
  2. std::unique_ptr differs between C++ versions
  3. std::unique_ptr may differ between debug/release builds.

This means if you use STL or boost in your DSO interface, ALL exes and dsos must use exactly the same version of the C++ runtime compiled with the same build flags (and same version of boost if that's your kind of thing).

I'd recommend using warning level 4 on Visual Studio, which will nicely list all of the above problems in your DSO interfaces (As C4251 warnings)

As for your question, yes the function will return a std::unique_ptr, however you are now allocating memory in the DSO, which you may be freeing in the exe. This can be very bad in the windows world, where you may find that debug builds have different heaps. Attempting to free the DSO allocated object in the EXE heap will throw a runtime error, but usually only in debug builds.

Your main should look like this:

void main_flow() {
auto handle = dlopen(...);
FuncPtr func = (FuncPtr)dlsym(handle, "some_function");
unique_ptr<Obj> obj = func();
}

Personally though, I'd recommend just returning a naked pointer, and doing a make_unique on it in your exe. That at least removes the C4251 problems, although you may get bitten by the heap issue (unless you make the destructor of the class type virtual)

Question about returning an unique_ptr from a function

1.There is no copy ctor for std::unique_ptr<T> indeed, there should not a function could accept a parameter declared as
std::unique_ptr<T>.

In fact, this is ok as long as you move the original std::unique_ptr to this local parameter

FoolFunc(std::move(uniq_ptr)); // ok
FoolFunc(std::unique_ptr<FooImage>{new FooImage}); // also ok

2.why

std::unique_ptr<T> f(std::unique_ptr<T> && ptr) { return ptr; }

does not compile?

Although the type of ptr is an rvalue reference, it is itself an lvalue, so return ptr will call the copy ctor, you need to use std::move to cast ptr to an rvalue again

std::unique_ptr<T> f(std::unique_ptr<T> && ptr) { 
return std::move(ptr);
}

The correct way of returning std::unique_ptr to an object of polymorphic class

You can do this:

return std::move(derived);

That way you tell the compiler no copy is needed, which satisfies the requirements of unique_ptr. If the types matched perfectly you should not need to explicitly specify move, but in this case you do.

function returning - unique_ptr VS passing result as parameter VS returning by value

In modern C++, the rule is that the compiler is smarter than the programmer. Said differently the programmer is expected to write code that will be easy to read and maintain. And except when profiling have proven that there is a non acceptable bottleneck, low level concerns should be left to the optimizing compilers.

For that reason and except if profiling has proven that another way is required I would first try option 3 and return a plain object. If the object is moveable, moving an object is generally not too expensive. Furthermore, most compilers are able to fully elide the copy/move operation if they can. If I correctly remember, copy elision is even required starting with C++17 for statements like that:

T foo = functionReturningT();

How can a unique_ptr be returned by value without std::move?

This is set out in the C++11 standard, § 12.8/32:

When the criteria for elision of a copy operation are met or would be
met save for the fact that the source object is a function parameter,
and the object to be copied is designated by an lvalue, overload
resolution to select the constructor for the copy is first performed
as if the object were designated by an rvalue
....

(emphasis mine). In plain english, it means that the lvalue p can be treated as an rvalue when it comes to overload resolution, because it is a candidate for copy elision. This in turn means the move constructor is picked up on overload resolution (in actual fact, the move copy is probably elided anyway.)

How can i return unique_ptr with pointer function?

You probably just want to return a non-owning pointer to the collection element right?

In that case just use .get():

Maths* Maths2::FindMathsID(int iID)
{
for (auto& it : l_MathsList)
{
if (it->IsMy(iID))
return it.get();
}
return nullptr;
}


Related Topics



Leave a reply



Submit