I have not been able to find any documentation about what happens if I pass a null pointer to std::make_unique.
Is an exception thrown?
std::make_unique forwards all the arguments passed to a matching target constructor.
Therefore, if you pass nullptr, it will search for a constructor that accepts nullptr_t (or any matching constructor). If it is not there, it will fail to compile.
class A
{
public:
A(void*) { ... } // could also be A(int*) or template <typename T> A(T*)
};
...
std::make_unique<A>(nullptr); // constructs an A by calling A(void*), passing nullptr.
std::make_unique is meant to construct an object wrapped by forwarding the arguments to the constructor of the wrapped object.
Passing nullptr to it doesn't make any sense in the way you mean. If you need to clear the content of a std::unique_ptr just call reset().
Related
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.
I want to create a class Bar that can distinguish between temporaries and non-const non-temporaries. According to this (about 25% down the page), I can get away with this if the second StealPointer takes a const reference (to a pointer in my case), but in my code it just uses the StealPointer(Foo*&& foo) version regardless of how it is called.
class Foo {};
class Bar {
public:
// For when StealPointer(new Foo()); is called. Bar instance then owns the
// pointer.
void StealPointer(Foo*&& foo) {} // Get the temporaries only.
// For when a Foo* already exists and is passed in StealPointer(my_foo_ptr);
// Takes ownership and invalidates the pointer that the caller once had.
void StealPointer(Foo*&) {} // Get lvalues only.
};
Can I do this? Is there a way to do this that only requires one function? If it matters, Bar is going to store the pointer in a unique_ptr and I would like to avoid the additional syntax of passing in a unique_ptr or making the caller do something with std::move. I can't just pass the pointers by reference, because temporaries of type Foo* can't be converted to Foo*&.
Make your function templated and let std::unique_ptr worry about those details for you.
template <typename Ptr>
void StealPointer(Ptr&& p) // a universal reference, matches *any* type of value
{
uniqptr = std::move(p); // Works for both rvalues and lvalues
}
I have a class with std::unique_ptr members which are initially initialized to nullptr and later assigned an actual object. They also use custom deleters.
I have two questions regarding this scenario: Should I use std::unique_ptr::reset() or copy assignment operator to assign a value to a unique pointer which was first initialized to nullptr? And if the latter is the case, then how can I make the code below work?
class MyClass {
private:
std::unique_ptr<Foo, void(*)(Foo*)> m_foo;
std::unique_ptr<Bar, void(*)(Bar*)> m_bar;
}
MyClass::MyClass() : m_foo(nullptr, ReleaseFoo), m_bar(nullptr, ReleaseBar) { }
bool MyClass::init()
{
m_foo.reset(CreateFoo()); // works ok
m_bar = std::unique_ptr<Bar, ReleaseBar>(CreateBar()); // 'ReleaseBar' is not a valid template type argument for parameter '_Dx'
}
Also, would the answer change if no custom deleters were involved (i.e. use reset with custom deleters and copy assignment otherwise)?
You can use resetor the move assignment operator to assign a new value.
The problem with your statement
m_bar = std::unique_ptr<Bar, ReleaseBar>(CreateBar()); // 'ReleaseBar' is not a valid template type argument for parameter '_Dx'
is, as the error message you quote indicates, that ReleaseBar is not a valid template argument. Earlier code uses ReleaseBar in a way consistent with this being a function name. In the earlier code the corresponding template argument was void(*)(Bar*).
Suppose I have a class called foo which inherits from a class called bar.
I have a std::unique_ptr to an instance of foo and I want to pass it to a function that only takes std::unique_ptr<bar>. How can I convert the pointer so it works in my function?
You can convert a std::unique_ptr<foo> rvalue to an std::unique_ptr<bar>:
std::unique_ptr<foo> f(new foo);
std::unique_ptr<bar> b(std::move(f));
Obviously, the pointer will be owned by b and if b gets destroyed bar needs to have a virtual destructor.
Nothing special is required because of the inheritance. You need to use std::move to pass the unique_ptr to a function, but this is true even if the types match:
#include <memory>
struct A {
};
struct B : A {
};
static void f(std::unique_ptr<A>)
{
}
int main(int,char**)
{
std::unique_ptr<B> b_ptr(new B);
f(std::move(b_ptr));
}
You may use this syntax:
std::unique_ptr<parent> parentptr = std::unique_ptr<child>(childptr);
Or you may use std::move.
The other option is to emit raw pointer, but you need to change a function:
void func(const parent* ptr)
{
// actions...
}
func(*childptr);
Here is a good article about smart pointers and passing it to functions: http://herbsutter.com/2013/06/05/gotw-91-solution-smart-pointer-parameters.
You can't, because it would violate the most basic unique_ptr rule: there has to be only one instance that holds a given pointer, and the unique_ptr has full ownership of it (when it goes out of scope, the pointee is deleted).
unique_ptr<T> and unique_ptr<U> (where U : T) aren't compatible, as you've seen.
For shared_ptr, for which you can have multiple instances, there is std::static_pointer_cast that behaves just like a static_cast, except that it accepts a shared_ptr and returns another one (and both point to the same object).
If you absolutely need to use a unique_ptr, you'll have to create a function that first disowns your current unique_ptr and puts that pointer into a new one of the right type. You might also need to do the opposite conversion after your function call.
I am trying to make use of the unique_ptr from C++x0, by doing
#include <memory>
and comping with -std=c++0x, however it is throwing up many errors this being an example.
/usr/lib/gcc/x86_64-redhat-linux/4.4.4/../../../../include/c++/4.4.4/bits/unique_ptr.h:214: error: deleted function ‘std::unique_ptr<_Tp, _Tp_Deleter>::unique_ptr(const std::unique_ptr<_Tp, _Tp_Deleter>&) [with _Tp = Descriptor, _Tp_Deleter = std::default_delete<Descriptor>]’
UPDATE****
This is what I am trying to do, I have removed typedefs so you can see clearly the types
static std::unique_ptr<SomeType> GetSomeType()
{
std::unique_ptr<SomeType> st("Data","Data2",false);
std::unique_ptr<OtherType> ot("uniportantconstructor data");
st->Add(ot);
return st;
}
//Public method of SomeType
void Add(std::unique_ptr<OtherType> ot)
{
//otmap is std::map<std::string,std::unique_ptr<OtherType> >
//mappair is std::Pair<std::string,std::unique_ptr<OtherType> >
otMap.Insert(mappair(ot->name(),ot));
}
UPDATE:
If my class SomeType has a method that returns a element from the map (using the key) say
std::unique_ptr<OtherType> get_othertype(std::string name)
{
return otMap.find(name);
}
that would enure the caller would recieve a pointer to the one in the map rather than a copy?
std::unique_ptr<OtherType> ot("unimportant constructor data");
st->Add(ot);
You cannot pass an lvalue to a function accepting a unique_pointer, because unique_pointer has no copy constructor. You must either move the lvalue (to cast it to an xvalue) or pass a prvalue:
// pass an xvalue:
std::unique_ptr<OtherType> ot("unimportant constructor data");
st->Add(std::move(ot));
// note: ot is now empty
// pass a prvalue:
st->Add(std::unique_ptr<OtherType>("unimportant constructor data"));
Inside the Add method, things are a little more complicated. First, you have to move from ot, because formal parameters are always lvalues (since they have names). Second, you cannot move from ot and get ot->name() as arguments to the mappair function, because the order of argument evaluation is unspecified in C++. So we have to get ot->name() in a separate statement before moving from ot:
void Add(std::unique_ptr<OtherType> ot)
{
auto name = ot->name();
otMap.Insert(mappair(name, std::move(ot)));
}
Hope this helps. Note that under no (sane) circumstances can two unique_ptr objects point to the same thing. If you need that functionality, unique_ptr is not what you want.
It looks like you're trying to use the copy constructor. There isn't one. If your calling code looks like this:
T *ptr = /* whatever */;
std::unique_ptr<T> up = ptr;
You have to change the second line to this:
std::unique_ptr<T> up (ptr);
The original version is (basically) implicitly turning the assignment into:
std::unique_ptr<T> up (std::unique_ptr<T>(ptr));
The copy constructor has been deleted. "deleted function" is C++0x-speak for explicitly removing an implicit special member function. In this case, the copy constructor.
As #Steve said you're probably using the copy constructor, unique_ptr does not support copy semantics, if you want to move to value to another unique_ptr you're have to move it.
unique_ptr<T> other = std::move(orig); // orig is now null