I was reading Top 10 dumb mistakes to avoid with C++11 smart pointer.
Number #5 reads:
Mistake # 5 : Not assigning an object(raw pointer) to a shared_ptr as
soon as it is created !
int main()
{
Aircraft* myAircraft = new Aircraft("F-16");
shared_ptr<aircraft> pAircraft(myAircraft);
...
shared_ptr<aircraft> p2(myAircraft);
// will do a double delete and possibly crash
}
and the recommendation is something like:
Use make_shared or new and immediately construct the pointer with
it.
Ok, no doubt about it the problem and the recommendation.
However I have a question about the design of shared_ptr.
This is a very easy mistake to make and the whole "safe" design of shared_ptr could be thrown away by very easy-to-detect missuses.
Now the question is, could this be easily been fixed with an alternative design of shared_ptr in which the only constructor from raw pointer would be that from a r-value reference?
template<class T>
struct shared_ptr{
shared_ptr(T*&& t){...basically current implementation...}
shared_ptr(T* t) = delete; // this is to...
shared_ptr(T* const& t) = delete; // ... illustrate the point.
shared_ptr(T*& t) = delete;
...
};
In this way shared_ptr could be only initialized from the result of new or some factory function.
Is this an underexploitation of the C++ language in the library?
or What is the point of having a constructor from raw pointer (l-value) reference if this is going to be most likely a misuse?
Is this a historical accident? (e.g. shared_ptr was proposed before r-value references were introduced, etc) Backwards compatibility?
(Of course one could say std::shared_ptr<type>(std::move(ptr)); that that is easier to catch and also a work around if this is really necessary.)
Am I missing something?
Pointers are very easy to copy. Even if you restrict to r-value reference you can sill easily make copies (like when you pass a pointer as a function parameter) which will invalidate the safety setup. Moreover you will run into problems in templates where you can easily have T* const or T*& as a type and you get type mismatches.
So you are proposing to create more restrictions without significant safety gains, which is likely why it was not in the standard to begin with.
The point of make_shared is to atomize the construction of a shared pointer. Say you have f(shared_ptr<int>(new int(5)), throw_some_exception()). The order of parameter invokation is not guaranteed by the standard. The compiler is allowed to create a new int, run throw_some_exception and then construct the shared_ptr which means that you could leak the int (if throw_some_exception actually throws an exception). make_shared just creates the object and the shared pointer inside itself, which doesn't allow the compiler to change the order, so it becomes safe.
I do not have any special insight into the design of shared_ptr, but I think the most likely explanation is that the timelines involved made this impossible:
The shared_ptr was introduced at the same time as rvalue-references, in C++11. The shared_ptr already had a working reference implementation in boost, so it could be expected to be added to standard libraries relatively quickly.
If the constructor for shared_ptr had only supported construction from rvalue references, it would have been unusable until the compiler had also implemented support for rvalue references.
And at that time, compiler and standards development was much more asynchronous, so it could have taken years until all compiler had implemented support, if at all. (export templates were still fresh on peoples minds in 2011)
Additionally, I assume the standards committee would have felt uncomfortable standardizing an API that did not have a reference implementation, and could not even get one until after the standard was published.
There's a number of cases in which you may not be able to call make_shared(). For example, your code may not be responsible for allocating and constructing the class in question. The following paradigm (private constructors + factory functions) is used in some C++ code bases for a variety of reasons:
struct A {
private:
A();
};
A* A_factory();
In this case, if you wanted to stick the A* you get from A_factory() into a shared_ptr<>, you'd have to use the constructor which takes a raw pointer instead of make_shared().
Off the top of my head, some other examples:
You want to get aligned memory for your type using posix_memalign() and then store it in a shared_ptr<> with a custom deleter that calls free() (this use case will go away soon when we add aligned allocation to the language!).
You want to stick a pointer to a memory-mapped region created with mmap() into a shared_ptr<> with a custom deleter that calls munmap() (this use case will go away when we get a standardized facility for shmem, something I'm hoping to work on in the next few months).
You want to stick a pointer allocated by into a shared_ptr<> with a custom deleter.
Related
As far as I understand, C++14 introduced std::make_unique because, as a result of the parameter evaluation order not being specified, this was unsafe:
f(std::unique_ptr<MyClass>(new MyClass(param)), g()); // Syntax A
(Explanation: if the evaluation first allocates the memory for the raw pointer, then calls g() and an exception is thrown before the std::unique_ptr construction, then the memory is leaked.)
Calling std::make_unique was a way to constrain the call order, thus making things safe:
f(std::make_unique<MyClass>(param), g()); // Syntax B
Since then, C++17 has clarified the evaluation order, making Syntax A safe too, so here's my question: is there still a reason to use std::make_unique over std::unique_ptr's constructor in C++17? Can you give some examples?
As of now, the only reason I can imagine is that it allows to type MyClass only once (assuming you don't need to rely on polymorphism with std::unique_ptr<Base>(new Derived(param))). However, that seems like a pretty weak reason, especially when std::make_unique doesn't allow to specify a deleter while std::unique_ptr's constructor does.
And just to be clear, I'm not advocating in favor of removing std::make_unique from the Standard Library (keeping it makes sense at least for backward compatibility), but rather wondering if there are still situations in which it is strongly preferred to std::unique_ptr
You're right that the main reason was removed. There are still the don't use new guidelines and that it is less typing reasons (don't have to repeat the type or use the word new). Admittedly those aren't strong arguments but I really like not seeing new in my code.
Also don't forget about consistency. You absolutely should be using make_shared so using make_unique is natural and fits the pattern. It's then trivial to change std::make_unique<MyClass>(param) to std::make_shared<MyClass>(param) (or the reverse) where the syntax A requires much more of a rewrite.
make_unique distinguishes T from T[] and T[N], unique_ptr(new ...) does not.
You can easily get undefined behaviour by passing a pointer that was new[]ed to a unique_ptr<T>, or by passing a pointer that was newed to a unique_ptr<T[]>.
The reason is to have shorter code without duplicates. Compare
f(std::unique_ptr<MyClass>(new MyClass(param)), g());
f(std::make_unique<MyClass>(param), g());
You save MyClass, new and braces. It costs only one character more in make in comparison with ptr.
Every use of new has to be extra carefully audited for lifetime correctness; does it get deleted? Only once?
Every use of make_unique doesn't for those extra characteristics; so long as the owning object has "correct" lifetime, it recursively makes the unique pointer have "correct".
Now, it is true that unique_ptr<Foo>(new Foo()) is identical in all ways1 to make_unique<Foo>(); it just requires a simpler "grep your source code for all uses of new to audit them".
1 actually a lie in the general case. Perfect forwarding isn't perfect, {}, default init, arrays are all exceptions.
Since then, C++17 has clarified the evaluation order, making Syntax A safe too
That's really not good enough. Relying on a recently-introduced technical clause as the guarantee of safety is not a very robust practice:
Someone might compile this code in C++14.
You would be encouraging the use of raw new's elsewhere, e.g. by copy-pasting your example.
As S.M. suggests, since there's code duplication, one type might get changed without the other one being changed.
Some kind of automatic IDE refactoring might move that new elsewhere (ok, granted, not much chance of that).
Generally, it's a good idea for your code to be appropriate/robust/clearly valid without resorting to language-laywering, looking up minor or obscure technical clauses in the standard.
(this is essentially the same argument I made here about the order of tuple destruction.)
Consider
void function(std::unique_ptr(new A()), std::unique_ptr(new B())) { ... }
Suppose that new A() succeeds, but new B() throws an exception: you catch it to resume the normal execution of your program. Unfortunately, the C++ standard does not require that object A gets destroyed and its memory deallocated: memory silently leaks and there's no way to clean it up. By wrapping A and B into std::make_uniques you are sure the leak will not occur:
void function(std::make_unique(), std::make_unique()) { ... }
The point here is that std::make_unique and std::make_unique are now temporary objects, and cleanup of temporary objects is correctly specified in the C++ standard: their destructors will be triggered and the memory freed. So if you can, always prefer to allocate objects using std::make_unique and std::make_shared.
[One answer, below is to force a choice between .release() and .get(), sadly the wrong general advice is to just use .get()]
Summary: This question is asking for technical reasons, or behavioural reasons (perhaps based on experience), why smart pointers such as unique_ptr are stripped of the major characteristic of a pointer, i.e. the ability to be passed where a pointer is expected (C API's).
I have researched the topic and cite two major claimed reasons below, but these hardly seem valid.
But my arrogance is not boundless, and my experience not so extensive as to convince me that I must be right.
It may be that unique_ptr was not designed simple lifetime management of dumb C API pointers as a main use, certainly this proposed development of unique_ptr would not be [http://bartoszmilewski.com/2009/05/21/unique_ptr-how-unique-is-it/], however unique_ptr claims to be "what auto_ptr should have been" (but that we couldn't write in C++98)" [http://www.stroustrup.com/C++11FAQ.html#std-unique_ptr] but perhaps that is not a prime use of auto_ptr either.
I'm using unique_ptr for management of some C API resources, and shocked [yes, shocked :-)] to find that so-called smart pointers hardly behave as pointers at all.
The API's I use expect pointers, and I really don't want to be adding .get() all over the place. It all makes the smart pointer unique_ptr seem quite dumb.
What's the current reasoning for unique_ptr not automatically converting to the pointer it holds when it is being cast to the type of that pointer?
void do_something(BLOB* b);
unique_ptr<BLOB> b(new_BLOB(20));
do_something(b);
// equivalent to do_something(b.get()) because of implicit cast
I have read http://herbsutter.com/2012/06/21/reader-qa-why-dont-modern-smart-pointers-implicitly-convert-to/ and it remarkably (given the author) doesn't actually answer the question convincingly, so I wonder if there are more real examples or technical/behavioural reasons to justify this.
To reference the article examples, I'm not trying do_something(b + 42), and + is not defined on the object I'm pointing to so *b + 42 doesn't make sense.
But if it did, and if I meant it, then I would actually type *b + 42, and if I wanted to add 42 to the pointer I would type b + 42 because I'm expecting my smart pointer to actually act like a pointer.
Can a reason for making smart pointers dumb, really be the fear that the C++ coder won't understand how to use a pointer, or will keep forgetting to deference it? That if I make an error with a smart pointer, it will silently compile and behave just as it does with a dumb pointer? [Surely that argument has no end, I might forget the > in ->]
For the other point in the post, I'm no more likely to write delete b than I am to write delete b.get(), though this seems to be a commonly proposed reason (perhaps because of legacy code conversions), and discussed C++ "smart pointer" template that auto-converts to bare pointer but can't be explicitly deleted however Meyers ambiguity of 1996, mentioned in http://www.informit.com/articles/article.aspx?p=31529&seqNum=7 seems to answer that case well by defining a cast for void* as well as for T* so that delete can't work out which cast to use.
The delete problem seems to have had some legitimacy, as it is likely to be a real problem when porting some legacy code but it seems to be well addressed even prior to Meyer (http://www.boost.org/doc/libs/1_43_0/libs/smart_ptr/sp_techniques.html#preventing_delete)
Are there more technical for denying this basic pointer-behaviour of smart pointers? Or did the reasons just seem very compelling at the time?
Previous discussions contain general warnings of bad things [Add implicit conversion from unique_ptr<T> to T* , Why doesn't `unique_ptr<QByteArray>` degrade to `QByteArray*`?, C++ "smart pointer" template that auto-converts to bare pointer but can't be explicitly deleted, http://bartoszmilewski.com/2009/05/21/unique_ptr-how-unique-is-it/] but nothing specific, or that is any worse than use of non-smart C pointers to C API's.
The inconvenience measured against the implied risks of coders blindly adding .get() everywhere and getting all the same harms they were supposed to have been protected against make the whole limitation seem very unworthwhile.
In my case, I used Meyer's trick of two casts, and take the accompanying hidden risks, hoping that readers will help me know what they are.
template<typename T, typename D, D F>
class unique_dptr : public std::unique_ptr<T, D> {
public: unique_dptr(T* t) : std::unique_ptr<T, D>(t, F) { };
operator T*() { return this->get(); }
operator void*() { return this->get(); }
};
#define d_type(__f__) decltype(&__f__), __f__
with thanks to #Yakk for the macro tip (Clean implementation of function template taking function pointer, How to fix error refactoring decltype inside template)
using RIP_ptr = unique_dptr<RIP, d_type(::RIP_free)>;
RIP_ptr rip1(RIP_new_bitmap("/tmp/test.png"));
No that's a smart pointer I can use.
* I declare the free function once when the smart pointer type is defined
* I can pass it around like a pointer
* It gets freed when the scope dies
Yes, I can use the API wrongly and leak owned references, but .get() doesn't stop that, despite it's inconvenience.
Maybe I should have some consts in there as a nod to lack of ownership-transference.
One answer that I'm surprised I haven't found in my searches is implied in the documentation for unique_ptr::release [http://en.cppreference.com/w/cpp/memory/unique_ptr/release]
release() returns the pointer and the unique_ptr then references nullptr and so clearly this can be used for passing on the owned reference to an API that doesn't use smart pointers.
By inference, get() is a corresponding function to pass unowned reference
As a pair, these functions explain why automatic de-referencing is not permitted; the coder is forced to replace each pointer use either with .release() or .get() depending on how the called function will treat the pointer.
And thus the coder is forced to take intelligent action and choose one behaviour or the other, and upgrade the legacy code to be more explicit and safe.
.get() is a weird name for that use, but this explanation makes good sense to me.
Sadly, this strategy is ineffective if the only advice the coder has is to use .get(); the coder is not aware of the choice and misses a chance to make his code safe and clear.
I am trying to create a function that takes an auto_ptr to Base class and I would like to call it with a auto_ptr to Derived class. However I am failing to get it done.
I have tried using it without a reference:
void function(std::auto_ptr<Base> ptr);
std::auto_ptr<Derived> derivedPtr( new ... )
function(derivedPtr); // error: #348: more than one user-defined conversion from
// "std::auto_ptr<Derived>" to "std::auto_ptr<Base>" applies
And with a reference:
void function(std::auto_ptr<Base>& ptr);
std::auto_ptr<Derived> derivedPtr( new ... )
function(derivedPtr); //error: #304: no instance of overloaded function "function"
// matches the argument list
EDIT:
I know auto_ptr is deprecated but I only have access to C++03 and cant access boost. So I would appreciate if the answers would focus on the question itself :) I also do understand the difference of a function taking a reference and a auto_ptr by value. In my actual code the function takes ownership of the auto_ptr so both are fine if I can get the conversion from Derived to Base working.
You are trying to auto_ptr by value and by reference, which are completely different things. The call by value means, you transfer ownership into the function, since auto_ptr does that on copy. Call by reference means there is only auto_ptr outside the function which keeps the ownership.
Since that difference is so very unintuitive, auto_ptr has been deprecated in the standard of 2011 and authors have been discouraging the use of auto_ptr much longer. In short:
Do not use auto_ptr at all.
Use a unique_ptr if you want to transfer ownership into the function, or a reference or plain pointer if you want to leave it outside the function. It depends on the actual case you have.
Update: since you are mentioning that you have to use C++03 without boost, there is a unique_ptr implementaion for C++03: http://howardhinnant.github.io/unique_ptr03.html
It uses a few boost features that can be handwritten easily, so it should be doable to port it to your plaform without having to use boost.
You can cast without ambiguity:
function(static_cast<std::auto_ptr<Base> >(derivedPtr));
Be aware that for this to work, Base must have a virtual destructor. Otherwise the code has undefined behavior.
The reason for the ambiguity is as Arne says in his answer -- it's not obvious to the caller whether function takes a value or a reference, and therefore it's not obvious to the caller whether their auto_ptr will be released or not by making the call, and if it is released whether it's converted to a type that can correctly delete the pointer. The ambiguous conversion AFAIK is there to stop you writing such difficult code. By casting, you make it explicit in the calling code that derivedPtr is released.
It is better to manage memory manually than use auto_ptras its use is so fraught with mistakes.
It is best to use an industrial strength replacememt: at the worst, examine the liberal boost liscense and determine if you can write a clone of your own.
Failimg that, write your own replavement owning_ptr. Create a ownomg_ptr_transfer class template as well. Block copy constructruction on owning_ptr, bit enable implicit conversion from owning_ptr_transfer which takes the underlying poimter. Have a free function or method that moves the underlying pointer to a transfer object.
In essence, if you must, write your own unique_ptr using C++03.
If you will not use someone else's solution, nor will you write your own, then simply mamahe memory manually. auto_ptr is not worth it: it is not just sub optimal compared to unique_ptr, it is actually harmful: its purpose is to make memory management easier, and instead it makes it more error prone. It was an experiment, and it failed.
With the new standard coming (and parts already available in some compilers), the new type std::unique_ptr is supposed to be a replacement for std::auto_ptr.
Does their usage exactly overlap (so I can do a global find/replace on my code (not that I would do this, but if I did)) or should I be aware of some differences that are not apparent from reading the documentation?
Also if it is a direct replacement, why give it a new name rather than just improve the std::auto_ptr?
You cannot do a global find/replace because you can copy an auto_ptr (with known consequences), but a unique_ptr can only be moved. Anything that looks like
std::auto_ptr<int> p(new int);
std::auto_ptr<int> p2 = p;
will have to become at least like this
std::unique_ptr<int> p(new int);
std::unique_ptr<int> p2 = std::move(p);
As for other differences, unique_ptr can handle arrays correctly (it will call delete[], while auto_ptr will attempt to call delete.
std::auto_ptr and std::unique_ptr are incompatible in someways and a drop in replacement in others. So, no find/replace isn't good enough. However, after a find/replace working through the compile errors should fix everything except weird corner cases. Most of the compile errors will require adding a std::move.
Function scope variable:
100% compatible, as long as you don't pass it by value to another function.
Return type:
not 100% compatible but 99% compatible doesn't seem wrong.
Function parameter by value:
100% compatible with one caveat, unique_ptrs must be passed through a std::move call. This one is simple as the compiler will complain if you don't get it right.
Function parameter by reference:
100% compatible.
Class member variable:
This one is tricky. std::auto_ptrs copy semantics are evil. If the class disallows copying then std::unique_ptr is a drop in replacement. However, if you tried to give the class reasonable copy semantics, you'll need to change the std::auto_ptr handling code. This is simple as the compiler will complain if you don't get it right. If you allowed copying of a class with a std::auto_ptr member without any special code, then shame on you and good luck.
In summary, std::unique_ptr is an unbroken std::auto_ptr. It disallows at compile time behaviors that were often errors when using a std::auto_ptr. So if you used std::auto_ptr with the care it needed, switching to std::unique_ptr should be simple. If you relied on std::auto_ptr's odd behavior, then you need to refactor your code anyway.
AFAIK, unique_ptr is not a direct replacement. The major flaw that it fixes is the implicit transfer of ownership.
std::auto_ptr<int> a(new int(10)), b;
b = a; //implicitly transfers ownership
std::unique_ptr<int> a(new int(10)), b;
b = std::move(a); //ownership must be transferred explicitly
On the other hand, unique_ptr will have completely new capabilities: they can be stored in containers.
Herb Sutter has a nice explanation on GotW #89:
What’s the deal with auto_ptr? auto_ptr is most charitably characterized as a valiant attempt to create a unique_ptr before C++
had move semantics. auto_ptr is now deprecated, and should not be used
in new code.
If you have auto_ptr in an existing code base, when you get a chance
try doing a global search-and-replace of auto_ptr to unique_ptr; the
vast majority of uses will work the same, and it might expose (as a
compile-time error) or fix (silently) a bug or two you didn't know you
had.
In other words, while a global search-and-replace may "break" your code temporarily, you should do it anyway: It may take some time to fix the compile errors, but will save you a lot more trouble in the long run.
In my opinion, a class should provide a well defined abstraction and no private members should be modified without the knowledge of class. But when I checked the "auto_ptr" (or any other smart pointer), this rule is violated. Please see the following code
class Foo{
public:
Foo(){}
};
int main(int argc, char* argv[])
{
std::auto_ptr<Foo> fooPtr(new Foo);
delete fooPtr.operator ->();
return 0;
}
The operator overload (->) gives the underlying pointer and it can be modified without the knowledge of "auto_ptr". I can't think it as a bad design as the smart pointers are designed by C++ geeks, but I am wondering why they allowed this. Is there any way to write a smart pointer without this problem.
Appreciate your thoughts.
There are two desirable properties a smart pointer should have:
The raw pointer can be retrieved (e.g. for passing to legacy library functions)
The raw pointer cannot be retrieved (to prevent double-delete)
Obviously, these properties are contradictory and cannot be realised at the same time! Even Boost's shared_ptr<Foo> et al. have get(), so they have this "problem." In practice, the first is more important, so the second has to go.
By the way, I'm not sure why you reached for the slightly obscure operator->() when the ordinary old get() method causes the same problem:
std::auto_ptr<Foo> fooPtr(new Foo);
delete fooPtr.get();
In order to provide fast, convenient, "pointer-like" access to the underlying object, operator-> unfortunately has to "leak" its abstraction a bit. Otherwise, smart pointers would have to manually wrap all of the members that are allowed to be exposed. These either requires a lot of "configuration" work on the part of those instantiating the smart pointer, or a level of meta-programming that just isn't present in C++. Besides, as pyrsta points out, even if this hole was plugged, there are still many other (perhaps non-standard) ways to subvert C++'s access control mechanisms.
Is there any way to write a smart pointer without this problem.
It isn't easy, and generally no (i.e., you can't do it for every, general Foo class).
The only way I can think of, to do this, would be by changing the declaration of the Foo class: make the Foo destructor private (or define a private delete operator as a member of the Foo class), and also specify in the declaration of the Foo class that std::auto_ptr<Foo> is a friend.
No, there's no way to completely prohibit such bad usage in C++.
As a general rule, the user of any library code should never call delete on any wrapped pointers unless specifically documented. And in my opinion, all modern C++ code should be designed so that the user of the classes never was left the full responsibility to manually release her acquired resources (ie. use RAII instead).
Aside note: std::auto_ptr<T> isn't the best option anymore. Its bad behaviour on copying can lead to serious coding errors. Often a better idea is to use std::tr1::scoped_ptr<T> or std::tr1::shared_ptr<T> or their Boost variants instead.
Moreover, in C++0x, std::unique_ptr<T> will functionally supercede std::auto_ptr<T> as a safer-to-use class. Some discussion on the topic and a recent C++03 implementation for unique_ptr emulation can be found here.
I don't think this shows that auto_ptr has an encapsulation problem. Whenever dealing with owned pointers, it is critical for people to understand who owns what. In the case of auto_ptr, it owns the pointer that it holds[1]; this is part of auto_ptr's abstraction. Therefore, deleting that pointer in any other way violates the contract that auto_ptr provides.
I'd agree that it's relatively easy to mis-use auto_ptr[2], which is very not ideal, but in C++, you can never avoid the fundamental issue of "who owns this pointer?", because for better or worse, C++ does not manage memory for you.
[1] Quote from cplusplus.com: "auto_ptr objects have the peculiarity of taking ownership of the pointers assigned to them": http://www.cplusplus.com/reference/std/memory/auto_ptr/
[2] For example, you might mistakenly believe that it has value semantics, and use it as a vector template parameter: http://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=4&ved=0CEEQFjAD&url=http%3A%2F%2Fwww.gamedev.net%2Ftopic%2F502150-c-why-is-stdvectorstdauto_ptrmytype--bad%2F&ei=XU1qT5i9GcnRiAKCiu20BQ&usg=AFQjCNHigbgumbMG3MTmMPla2zo4LhaE1Q&sig2=WSyJF2eWrq2aB2qw8dF3Dw
I think this question addresses a non-issue. Smart pointers are there to manage ownership of pointers, and if doing so they make the pointer inaccessible, they fail their purpose.
Also consider this. Any container type gives you iterators over them; if it is such an iterator then &*it is a pointer to an item in the container; if you say delete &*it then you are dead. But exposing the adresses of its items is not a defect of container types.