Is it possible to move a moveable heap instance into a local object? I have the example code below:
#include <memory>
#include <type_traits>
#include <concepts>
#include <string>
template<std::move_constructible T>
struct LazyGuard
{
T value;
LazyGuard(T* const ptr) : value(std::move(*ptr)) { }
};
template <std::move_constructible T>
inline T&& MakeLocal(T* ptr)
{
return std::move(*ptr);
}
int main()
{
LazyGuard<std::string> guard(new std::string("Will be using with 'Socket* TCPSocket::FromAddress' kind of constructors"));
// Or another way
std::string local2 = MakeLocal(new std::string("similar thing"));
// Or another way
std::string local3(MakeLocal(new std::string("similar thing")));
return 0;
}
What I want to know is:
Can we guarantee the move constructor (or move assignment operator) will be called?
Is the code well-defined, or can it be made well-defined?
I know it is possible to use smart pointers, but it is not what I am asking here.
Update: To fix the leak problem I am adding delete in the guard constructor so the improved example below. I could not find a way to do this with "MakeLocal" example is this possible in any way?:
template<std::move_constructible T>
struct LazyGuard
{
T value;
LazyGuard(T* const ptr) : value(std::move(*ptr)) { delete ptr; }
};
int main()
{
LazyGuard<std::string> guard(new std::string("Will be using with 'Socket* TCPSocket::FromAddress' kind of constructors"));
return 0;
}
CodePudding user response:
Can we guarantee the move constructor (or move assignment operator) will be called?
Yes
Heap vs stack only "means" anything as far as memory allocation/deallocation is concerned. Apart from that, they are effectively indistinguishable. An object is an object with all that this entails no matter where its memory is located.
There is no distinction between moving objects from the heap to the heap, from the stack to the stack, from the heap to the stack, or from the stack to the heap.
In short: Yes, you can safely move-construct or move-assign a stack-based object from an instance living on the heap.
Is the code well-defined, or can it be made well-defined?
It is well-defined , but not well-behaved.
Note that I said move-construct. This does NOT destroy the object on the heap, just moves its content to the stack. You still need to destroy the original object.
As it stands, your code is leaking a bunch of memory.
Edit: The updated constructor of LazyGuard
will work without leaking memory. However, a constructor taking ownership of a raw pointer is very, very bad design.
LazyGuard
should really be written like this:
template<std::move_constructible T>
struct LazyGuard
{
T value;
LazyGuard(T v) : value(std::move(v)) {}
};
Where its initializer comes from is none of its business, that's a problem for where it is used.