Prevent copy-construction with make_shared - c++

I have a manager class that allow clients to add components through two methods: one with no argument that default-construct the component and another one that takes an rvalue (which should allow client to use a custom constructor of the component).
Here's the code I came up with:
template <class TComponent>
std::shared_ptr<TComponent> AddComponent()
{
return AddComponent(TComponent{ this });
}
template <class TComponent>
std::shared_ptr<TComponent> AddComponent(const TComponent &&obj)
{
auto ptr = std::make_shared<TComponent>(obj);
vec.push_back(ptr);
return ptr;
}
The problem I have is that std::make_shared always copy-constructs the object. Is there a way to prevent this behavior?
I read about perfect forwarding but it doesn't seem to help here.

I read about perfect forwarding but it doesn't seem to help here.
I don't see why it wouldn't.
Simply remove the const to make move construction possible, and add std::forward:
template <class TComponent>
std::shared_ptr<TComponent> AddComponent(TComponent &&obj)
{
auto ptr = std::make_shared<TComponent>(std::forward<TComponent>(obj));
vec.push_back(ptr);
return ptr;
}
Now, rvalues will be moved. Lvalues will be copied which you cannot avoid.

Related

Is std::optional<std::shared_ptr<MyClass>> good for avoiding null point deference?

I want to write safe C++ programs, therefore:
I wanted to avoid memory leaks, so I started using std::shared_ptr.
However, I still had some null pointer deferences some times. I've come up with the idea of using using MyClassSafe = std::optional<std::shared_ptr<MyClass>>.
Then I avoid both memory leaks and null pointer deference. Well, kind of. For example:
MyClassSafe myClassSafe = std::make_shared<MyClass>();
//Then everytime I want to use myClassSafe:
if (myClassSafe) {
//use it here
} else {
//do something in case no value
}
//However, this situation can be possible:
MyClassSafe notVerySafe = std::make_shared<MyClass>(nullptr); // or = std::shared_ptr(nullptr);
if (myClassSafe) {
//use it here, for example:
//this deferences a nullptr
myClassSafe.value()->someFunction();
} else {
//do something in case no value
}
so this is not much safer. It's better but I still can make mistakes.
I can imagine a safe_shared_ptr<T> class that instead of calling the object's functions on operator->, it could return std::optional<T&> (much like Rust) for which we can then safely call or deal with the std::nullopt case. Isn't there something already in C++? Or can it be implemented easily?
You haven't shown need for either pointers or optionals here.
MyClass myClassSafe;
myClassSafe.someFunction();
No possibility of null pointers or empty optionals in sight.
optional<T> allows you to handle the "no T available" case, which shared_ptr<T> already handles. Therefore optional<shared_ptr<T>> is redundant, just like optional<optional<T>> is.
There is a case to be made for shared_ptr<optional<T>> - if one owner creates the T object, the other owner can see the new object, so that isn't really redundant.
Your use of std::optional here is the cause of the problem. std::shared_ptr defines operator bool as a null pointer check, but because you have wrapped it in std::optional this never gets called
If instead you try:
MyClass myClass = std::make_shared<MyClass>(nullptr); // or = std::shared_ptr(nullptr);
if (myClass) {
// std::shared_ptr implements operator bool as a null pointer check
myClass->someFunction();
} else {
//do something in case no value
}
Isn't there something already in C++?
There is nothing in std to handle non null smart pointer.
As Caleth shows in his answer, you can use object directly and avoid (smart) pointer and std::optional.
Or can it be implemented easily?
Non null smart pointer (a "smart reference" :) ) should be non default constructible, and "non-movable" (I mean move should not invalidate the reference).
You could implement it with existing smart pointer, something like:
template <typename T>
class unique_ref
{
public:
// Avoid variadic constructor which might take precedence over regular copy/move constructor
// so I use tag std::in_place_t here.
template <typename ... Ts>
unique_ref(std::in_place_t, Ts&&... args) : std::make_unique<T>(std::forward<Ts>(args)...) {}
unique_ref(const unique_ref&) = delete;
unique_ref(unique_ref&&) = delete;
unique_ref& operator=(const unique_ref&) = delete;
unique_ref& operator=(unique_ref&&) = delete;
const T& operator*() const { return *ptr; }
T& operator*() { return *ptr; }
const T* operator ->() const { return ptr.get(); }
T* operator*() { return ptr.get(); }
private:
std::unique_ptr<T> ptr;
};
template <typename T, typename ... Ts>
unique_ref<T> make_unique_ref(Ts&&... args)
{
return {std::in_place, std::forward<Ts>(args)...};
}
unique version is not much useful, as non-copyable, non-movable. using directly T seems simpler.
shared version is copyable (its move should do identical to the copy)
A weak version might return an std::optional<shared_ref<T>>.

Using try_emplace with a shared_ptr

So I have a std::unordered_map<std::string, std::shared_ptr<T>> and I'd like to use try_emplace to add/reference elements. Previously, it was simply a std::unordered_map<std::string, T> and T has a constructor which just takes a std::string, so I was using myMap.try_emplace(myString, myString).first->second to reference the T and create it if necessary.
However, if I simply change it to myMap.try_emplace(myString, std::make_shared<T>(myString)).first->second, it, of course, constructs the T every time.
What's the most idiomatic way to get around this? To clarify, I'd like to construct the object on the heap with a shared pointer and insert the shared pointer into the unordered map if and only if there is not already a matching element in the map.
You can use operator[] instead:
auto &ptr = map[ key ];
if( !ptr ) ptr = std::make_shared<T>( myString );
Note: this solution came with assumption that you do not want to keep default constructed std::shared_ptr in the map. If that is the case, then you would need to use little more verbose code with try_emplace:
auto p = map.try_emplace( key, nullptr );
if( p.second ) p.first->second = std::make_shared<T>( myString );
else {
if( !p.first->second ) { // handle nullptr }
}
auto &ptr = p.first->second;
Write a factory function wrapper:
template<class F>
struct auto_factory_t {
F f;
operator decltype(std::declval<F&&>()()) && {
return std::move(f)();
}
};
template<class F>
auto_factory_t<std::decay_t<F>> wrap_factory(F&&f){
return {std::forward<F>(f)};
}
then
myMap.try_emplace(myString, wrap_factory([&]{return std::make_shared<T>(myString);})).first->second;
and done.
wrap_factory(f) returns an object containing f. It can be implicitly cast into the type f() returns, and when done so calls f().
Another option is to wrap the shared pointer in your own type. Then you can provide a constructor that takes a T and makes a shared_ptr<T>. This lets you use try_emplace and pass to it a T without having to have anything actually created if the object already exists. There is a lot of boiler plate involved in this though if you want to use the object as if it were a shared_ptr<T>.
What you may do:
// C++17, else you have to move declaration outside of the `if`
// and retrieve it/inserted from pair result.
if (auto [it, inserted] = myMap.try_emplace(myString, nullptr); inserted) {
it->second = std::make_shared<T>(myString);
}
Modern C++17 approach
template <class factory>
class deferred_factory {
public:
deferred_factory(factory&& mfactory) : mFactory{std::move(mfactory)} {}
// Wrap invocation of #c mFactory in an appropriate conversion operator.
/* implicit */ operator std::invoke_result_t<factory>() {
return mFactory();
}
private:
factory mFactory;
};
// Deduction guide for lvalue reference factories.
template <class factory>
deferred_factory(const factory&) -> deferred_factory<const factory&>;
Explanations
The basic trick is delaying initialization, i.e. invoking the factory. This is done by initializing the value type from another type (deferred_factory) which is implicitly convertible to the desired value type.
The conversion operator returns exactly the same type as invoking the factory directly. Due to mandatory copy/move elision with C++17, this imposes no extra copies whatsoever (no further && / std::move required).
With class template argument deduction, no further helpers for creating deferred_factorys are necessary. The final deduction guide allows reusing existing factories for multiple deferred_factorys which only wrap a reference to the original factory.
Usage
In OP's context
myMap.try_emplace("key", deferred_factory{[]{return std::make_shared<T>("value");}});
inserts "key" into myMap if and only if it hasn't been present before. The shared_ptr<T> is only ever created upon insertion.
This answer is a modern variant of Yakk - Adam Nevraumont's. All credit to him.

Does an std::optional parameter create a copy?

I have a function that takes an std::optional
void foo(const std::optional<T>& opt);
But copying T is expencive.
Does this create a copy of T?
If so, how can I not create a copy?
As clarified by your follow up comment, you are calling the function foo like this:
T t;
...
foo({t});
In this case, the answer is yes. A copy of t will be created. To avoid that, you can use std::reference_wrapper to avoid copying:
void foo(const std::optional<std::reference_wrapper<const T>> &optT) {
...
}
With these changes to the function, calling the it in the same manner will not result in creation of a copy.
Yes, std::optional stores a copy of whatever you pass to it. The C++17 standard explicitly prohibits storing references in std::optional. See: https://en.cppreference.com/w/cpp/utility/optional#:~:text=There%20are%20no%20optional%20references
As suggested by others, passing std::optional<std::reference_wrapper<const T>> is one way to avoid making copies.
void foo(const std::optional<std::reference_wrapper<const T>>& opt) {
if (opt) {
// Do something.
}
}
T t;
foo(t);
But consider that the C++ committee had good reasons for disallowing references in std::optional. For instance, an "optional reference" essentially describes what a plain pointer does, and plain pointers don't suffer from the long type name.
void foo(const T* opt) {
if (opt) {
// Do something.
}
}
T t;
foo(&t);
I think you will find useful this documentation page. See the section about "Optional function parameters".
When you call the function and pass an instance of T, an optional will be constructed which will own its own copy of T, therefore it will call T's copy constructor.
int main()
{
T t;
optional<T> ot;
foo(t); // will create a copy
foo(ot); // won't create a copy
}
If you're using boost::optional, not std::optional, you can declare foo as receiving an optional reference, i.e.
void foo(boost::optional<const T&> t)

C++ std::move a pointer

I have a C++ framework which I provide to my users, who should use a templated wrapper I wrote with their own implementation as the templated type.
The wrapper acts as an RAII class and it holds a pointer to an implementation of the user's class.
To make the user's code clean and neat (in my opinion) I provide a cast operator which converts my wrapper to the pointer it holds. This way (along with some other overloads) the user can use my wrapper as if it is a pointer (much like a shared_ptr).
I came across a corner case where a user calls a function, which takes a pointer to his implementation class, using std::move on my wrapper. Here's an example of what it looks like:
#include <iostream>
using namespace std;
struct my_interface {
virtual int bar() = 0;
};
template <typename T>
struct my_base : public my_interface {
int bar() { return 4; }
};
struct my_impl : public my_base<int> {};
template <typename T>
struct my_wrapper {
my_wrapper(T* t) {
m_ptr = t;
}
operator T*() {
return m_ptr;
}
private:
T* m_ptr;
};
void foo(my_interface* a) {
std::cout << a->bar() << std::endl;
}
int main()
{
my_impl* impl = new my_impl();
my_wrapper<my_impl> wrapper(impl);
foo(std::move(wrapper));
//foo(wrapper);
return 0;
}
[This is ofcourse just an example of the case, and there are more methods in the wrapper, but I'm pretty sure that don't play a role here in this case]
The user, as would I, expect that if std::move was called on the wrapper, then after the call to foo the wrapper will be empty (or at least modified as if it was moved), but in reality the only method being invoked before foo is the cast operator.
Is there a way to make the call to foo distinguishable between the two calls to foo i.e when calling with and without std::move?
EDIT
Thanks to the Mooing Duck's comment I found a way that my_wrapper knows which call is required, but I'm really not sure this is the best method to go with and will appreciate comments on this as well:
Instead of the previous cast operator use the following two:
operator T*() & {
return m_ptr;
}
operator T*() &&{
//Do something
return m_ptr;
}
now operator T*() && is called when calling with std::move and operator T*() & is called when calling without it.
The user, as would I, expect that if std::move was called on the wrapper, then after the call to foo the wrapper will be empty (or at least modified as if it was moved)
Your expectation is wrong. It will only be modified if a move happens, i.e. if ownership of some kind of resource is transferred. But calling foo doesn't do anything like that, because it just gets access to the pointer held inside the wrapper. Calling std::move doesn't do anything except cast its argument to an rvalue, which doesn't alter it. Some function which accepts an rvalue by reference might modify it, so std::move enables that, but it doesn't do that itself. If you don't pass the rvalue to such a function then no modification takes place.
If you really want to make it empty you can add an overload to do that:
template<typename T>
void foo(my_wrapper<T>&& w) {
foo(static_cast<my_interface*>(w));
w = my_wrapper<T>{}; // leave it empty
}
But ... why? Why should it do that?
The wrapper isn't left empty if you do:
my_wrapper<my_impl> w(new my_impl);
my_wrapper<my_impl> w2 = std::move(w);
And isn't left empty by:
my_wrapper<my_impl> w(new my_impl);
my_wrapper<my_impl> w2;
w2 = std::move(w);
If copying an rvalue wrapper doesn't leave it empty, why should simply accessing its member leave it empty? That makes no sense.
Even if your wrapper has a move constructor and move assignment operator so that the examples above do leave w empty, that still doesn't mean that accessing the member of an rvalue object should modify the object. Why does it make any logical difference whether the operator T* conversion is done to an lvalue or an rvalue?
(Also, are you really sure that having implicit conversions both to and from the wrapped pointer type is a good idea? Hint: it's not a good idea. In general prefer to make your conversions explicit, especially if you're dealing with pointers to dynamically-allocated objects.)

Extending lifetime of temporary - proper way

Say I have:
f(T& t){ ... }
which I sometimes want to provide argument for from a function call
T GetT() { ... }
like this:
f(GetT())
Which won't compile even though I'm convinced that the lifetime of T is guaranteed to last until the end of expression, I also cannot change the T& to const T& because I need to modify the object inside f.
I thought about using T&& but then when I happen to have lvalue of T I would need to move() it, which would make it awkward because I sometimes need to use T after the call to f. In my case T is plain old data so I guess it would work but it doesn't feel right to move an object and use it afterwards.
Is there an elegant way to allow function to take both rval and lval refs to mutable object?
ATM I'm just doing
T t = GetT();
f(t);
Which I feel is at least one useless line and one useless copy of T.
How about using universal references.
Not sure if you would consider it elegant but it would look something like this:
struct MyStruct
{
int i;
};
template<class T>
void foo(T&& t)
{
static_assert(std::is_base_of<MyStruct,
typename std::remove_reference<T>::type>::value, "ERROR");
t.i = 1024;
}
MyStruct GetMyStruct()
{
return MyStruct();
}
int main()
{
foo(GetMyStruct());
MyStruct ms;
foo(ms);
return 0;
}
The way that you're currently doing, i.e. storing the object in a variable, is a proper way to extend the lifetime of the returned object and allow non-const references to bind to it. If GetT is implemented so that (N)RVO is possible, then no useless copy need to be made because it can be elided.
I came up with a solution to my problem with simple overload:
f(T& t) { ... }
f(T&& t) { f(t); }
But there still has to be a better way.
You cannot bind a reference to a temporany: only const reference can do that.
I suggesto you to look this link C++11 binding rules for const &&