It could be implemented thusly:
std::shared_ptr<T> operator->() {
auto shared = lock();
if(shared == nullptr) {
throw std::bad_weak_ptr(); // or some other exception
}
return shared;
}
Live Demo
Why did the authors of weak_ptr decide to not have operator->? (They must have thought of it)
I can think of potential reasons but I wonder what the official reason is, if one exists. Potential reasons:
Discourage extra increment/decrement of reference count for multiple calls
Encourage explicit locking rather than (somewhat hidden) exceptions
If you are confused about the lifetime of the returned shared_ptr, see this paper.
Also, someone asked why would one use a weak_ptr if you expect it to not be expired? Answer: cycles.
The original proposal weak_ptr didn't include an overload of operator->.
I haven't looked through the minutes of every meeting since, but have followed what's been discussed, and don't recall a mention of anybody having proposed that it should be added. As such, the "official" reason it's not present is probably largely that nobody's proposed that it be added.
If you want to go back to the very beginning, most of this stems from John Ellis and David Detlef's Safe, Efficient Garbage Collection for C++ paper, from Usenix 1994. That included a weakptr type in its Appendix B. That's somewhat different (weakptr::pointer returns a pointer directly, or a null-pointer if the pointee has already been destroyed), but still didn't use an operator overload to do the job.
Greg Colvin wrote the original proposal to add counted_ptr to the standard. Its counted_ptr was essentially equivalent to what's now called shared_ptr, but did not include anything analogous to weak_ptr.
Shortly after the committee rejected the counted_ptr proposal and adopted auto_ptr instead, the basic idea of counted_ptr was revived on Boost. I don't remember seeing any discussion of adding an operator-> to it, but it "lived" there for so long that it's entirely possible somebody could have proposed it without my being aware of it.
I'll take a shot at giving a good reason why this is not a good idea:
One thing is clarity:
ptr->foo();
ptr->bar();
The problem here is that somewhere between the first and second call, ptr might expire, either by a different thread (which would be a race condition) or by a sideeffect of the call to foo.
Another thing is symmetry: When I have a pointer, I expect operators *, -> and an implicit conversion to a boolean value. Some might disagree, but operators * and -> often coincide. I'd be surprised that this isn't the case here.
That said, with C++11, it's just too easy to write:
if (auto p = ptr.lock()) {
p->foo();
p->bar();
}
Knowing that ptr is a weak_ptr, the meaning and behaviour of that code is pretty clear.
Related
In his excellent book "C++ Concurrency in Action" (2nd edition including C++17) Anthony Williams discusses the implementation of a thread-safe stack.
In the course of this, he proposes an adapter implementation of std::stack, which, among other things, would combine the calls of top() and pop() into one. The separation into 2 separate functions, however, was done for a reason in std::stack, namely to avoid losing data in the case that a potential copy made when returning the popped element to the caller throws an exception inside the copy constructor. When returning, the element will have already been popped off and is consequentially lost.
Instead of having a function T pop(), he proposes other variations of pop that would be able to remove the element off the stack and provide it to the caller in one operation, all of which come with their own problems, though.
The 1st alternative he proposes has the signature void pop(T&). The caller passes in a reference to a T and gets the popped off object that way. This way of doing it, however, comes with the problem that a T need be constructed prior to the call to pop, which might be an expensive operation, or it might not be possible to construct a T beforehand at all because necessary data might not be available yet at the time. Another problem the author mentions is that T might not be assignable, which would be required for this solution, though.
Now my question: Wouldn't all of the mentioned problems be solved if we passed a std::optional<T>& instead of a T&?
In that case, no instance of T would need to be constructed prior to the call to pop. Furthermore, assignability would not be required anymore either, since the object to be returned could be constructed into the std::optional<T> instance directly using its emplace function.
Am I missing something crucial here or am I right? If I am indeed right, I would be curious to know why this was not considered (for a good reason or just plainly an oversight?).
std::optional does solve all of the mentioned problems, and using it to control lifetime can be quite valuable, although it would appear a bit strange in
std::optional<T> o;
st.pop(o);
to have o always engaged.
That said, with a stupid scope-guard trick it's possible in C++17 to safely return T even without requiring no-throw-movability:
T pop() {
struct pop_guard {
C &c;
int u=std::uncaught_exceptions();
~pop_guard() {if(std::uncaught_exceptions()==u) c.pop_back();}
} pg{c};
return std::move(c.back());
}
(We could of course test for a throwing move and just move (perhaps twice) in its absence.)
However, what wasn't mentioned is that separate top and pop allows a T that isn't even movable, so long as the underlying container supports it. std::stack<std::mutex> works (with emplace, not push!) because std::deque doesn't require movability.
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 currently looking at the most popular smart Ptr implementations such as boost shared and weak pointers aswell as loki Smart and Strong pointer since I want to implement my own and from what I understand Loki Strong pointer looks unsafe to me but I rather think that I understand it wrong so I'd like to discuss whether it's safe or not. The reason why I think it's not safe is that as far as I can tell it does not treat weak Pointers (that is a StrongPtr, where false indicates its weak) with enough care:
for instance the dereferencing functions:
PointerType operator -> ()
{
KP::OnDereference( GetPointer() ); //this only asserts by default as far as i know
//could be invalidated right here
return GetPointer();
}
In a multithreaded environment a weak pointer could be invalidated at any time, so that this function might return an invalidated Ptr.
As far as my understanding goes you would either have to create a strongPtr instance of the ptr you are dereferencing to ensure that it does not get invalidated half way through. I think thats also the reason why boost does not allow you to dereference a weak_ptr without creating a shared_ptr instance first. Lokis StrongPtr Constructor suffers from the same problem I think.
Is this a problem or am I reading the src wrong?
Regarding the use of assert, it's a programming error to use operator-> on an empty StrongPtr<> instance; i.e., it is the caller's responsibility to ensure that the StrongPtr<> instance is non-empty before dereferencing it. Why should anything more than an assert be needed? That said, if you deem some other behavior more appropriate than assert, then that's exactly what the policy is for.
This is a fundamental difference between preconditions and postconditions; here's a long but very good thread on the subject: comp.lang.c++.moderated: Exceptions. Read in particular the posts by D. Abrahams, as he explains in detail what I'm stating as understood fact. ;-]
Regarding the thread-safety of StrongPtr<>, I suspect most of Loki predates any serious thread-safety concerns; on the other hand, boost::shared_ptr<> and std::shared_ptr<> are explicitly guaranteed to be thread-safe, so I'm sure their implementations make for a "better" (though much more complicated) basis for study.
After reading carefully, I think I saw the rationale.
StrongPtr objects are dual in that they represent both Strong and Weak references.
The mechanism of assert works great on a Strong version. On a Weak version, it is the caller's responsability to ensure that the object referenced will live long enough. This can be achieved either:
automatically, if you know that you have a Strong version
manually, by creating a Strong instance
The benefit wrt std::shared_ptr is that you can avoid creating a new object when you already know that the item will outlive your use. It's an arguable design decision, but works great for experts (of which Alexandrescu undoubtebly is). It may not have been targetted at regular users (us) for which enforcing that a Strong version be taken would have been much better imho.
One could also argue that it's always easier to criticize with the benefit of hindsight. Loki, for all its greatness, is old.
I thought dereferencing a NULL pointer was dangerous, if so then what about this implementation of an auto_ptr?
http://ootips.org/yonat/4dev/smart-pointers.html
If the default constructor is invoked without a parameter the internal pointer will be NULL, then when operator*() is invoked won't that be dereferencing a null pointer?
Therefore what is the industrial strength implementation of this function?
Yes, dereferencing NULL pointer = bad.
Yes, constructing an auto_ptr with NULL creates a NULL auto_ptr.
Yes, dereferencing a NULL auto_ptr = bad.
Therefore what is the industrial strength implementation of this function?
I don't understand the question. If the definition of the function in question created by the industry itself is not "industrial strength" then I have a very hard time figuring out what might be.
std::auto_ptr is intended to provide essentially the same performance as a "raw" pointer. To that end, it doesn't (necessarily) do any run-time checking that the pointer is valid before being dereferenced.
If you want a pointer that checks validity, it's relatively easy to provide that, but it's not the intended purpose of auto_ptr. In fairness, I should add that the real intent of auto_ptr is rather an interesting question -- its specification was changed several times during the original standardization process, largely because of disagreements over what it should try to accomplish. The version that made it into the standard does have some uses, but quite frankly, not very many. In particular, it has transfer-of-ownership semantics that make it unsuitable for storage in a standard container (among other things), removing one of the obvious purposes for smart pointers in general.
Its purpose to help prevent memory leaks by ensuring that delete is performed on the underlying pointer whenever the auto_ptr goes out of scope (or itself is deleted).
Just like in higher-level languages such as C#, trying to dereference a null pointer/object will still explode, as it should.
Do what you would do if you dereferenced a NULL pointer. On many platforms, this means throw an exception.
Well, just like you said: dereferencing null pointer is illegal, leads to undefined behavior. This immediately means that you must not use operator * on a default-constructed auto_ptr. Period.
Where exactly you see a problem with "industrial strength" of this implementation is not clear to me.
#Jerry Coffin: It is naughty of me to answer your answer here rather than the OP's question but I need more lines than a comment allows..
You are completely right about the ridiculous semantics of the current version, it is so completely rotten that a new feature: "mutable" HAD to be added to the language just to allow these insane semantics to be implemented.
The original purpose of "auto_ptr" was exactly what boost::scoped_ptr does (AFAIK), and I'm happy to see a version of that finally made it into the new Standard. The reason for the name "auto_ptr" is that it should model a first class pointer on the stack, i.e. an automatic variable.
This auto_ptr was an National Body requirement, based on the following logic: if we have catchable exceptions in C++, we MUST have a way to manage pointers which is exception safe IN the Standard. This also applies to non-static class members (although that's a slightly harder problem which required a change to the syntax and semantics of constructors).
In addition a reference counting pointer was required but due to a lot of different possible implementation with different tradeoffs, one can accept that this be left out of the Standard until a later time.
Have you ever played that game where you pass a message around a ring of people and at the end someone reads out the input and output messages? That's what happened. The original intent got lost because some people thought that the auto_ptr, now we HAD to have it, could be made to do more... and finally what got put in the standard can't even do what the original simple scope_ptr style one did (auto_ptr semantics don't assure the pointed at object is destroyed because it could be moved elsewhere).
If I recall the key problem was returning the value of a auto_ptr: the core design simply doesn't allow that (it's uncopyable). A sane solution like
return ap.swap(NULL)
unfortunately still destroys the intended invariant. The right way is probably closer to:
return ap.clone();
which copies the object and returns the copy, destroying the original: the compiler is then free to optimise away the copy (as written not exception safe .. the clone might leak if another exception is thrown before it returns: a ref counted pointer solves this of course).