And maybe they shouldn't like shared_ptr so much! It's a source of memory lifecycle bugs; everything touched by shared_ptr needs to be under the shared_ptr regime or carefully bridged to it.
boost::bind seems more like evidence in the case against C++ than an advertisement for boost.
"everything touched by shared_ptr needs to be under the shared_ptr regime or carefully bridged to it"
Well, yes. That's sort of the point. You can write old-style C++, or your can write modern C++, but the library designers are trying to make you think carefully when you're doing both, because that is a source of memory lifecycle bugs. Using the boost (now standard) pointers universally tends to dramatically reduce the chances of memory-related bugs in C++ code.
Not using shared_ptr is a code smell. Sometimes you have to do it, but if you're avoiding the use of the smart pointer classes in blue sky code, you're doing something wrong.
The main thing that sucks about shared_ptr's is that they break C++'s limited support for covariance. For example
class A { ... };
class B : public A { ... };
// Totally works
class AFactory {
public:
virtual A* makeOne();
}
class BFactory : public AFactory {
public:
virtual B* makeOne();
}
// totally doesn't
class SharedAFactory {
public:
virtual boost::shared_ptr<A> makeOne();
};
class SharedBFactory {
public:
virtual boost::shared_ptr<B> makeOne();
};
Also, from a performance perspective, locking the memory bus to do atomic operations on a shared_ptr totally blows when you're expected to turn a request around in a few microseconds.
I'll grant you the larger point that inferring relationships by template value type can be a pain. But this example is kind of bad. First off, I assume you meant to derive SharedBFactory from SharedAFactory? Otherwise, it works fine.
If not, the only problem here is that C++ won't let you have a virtual function override that only differs by return type. And since it can't tell that shared_ptr<A> and shared_ptr<B> are related by value_type, it complains (this is the larger point that I granted you earlier). But the type theatrics are hiding a bigger problem....
In this example, you can either fix the type error by making makeOne() return a boost::shared_ptr<A> in all factories (in which case, you really do want a polymorphic interface), or you can make a new function that returns a different pointer type (in which case, you don't). The type system here is doing you a favor, and telling you that your design is bogus -- you shouldn't be creating a polymorphic factory interface that returns different types (because that isn't actually polymorphic, is it?)
This isn't totally academic: I find that in nearly all type-error situations like this one, the real problem can be traced back to sloppy coding.
"locking the memory bus to do atomic operations on a shared_ptr totally blows when you're expected to turn a request around in a few microseconds."
There's always a cost to reference counting, and therefore, it's not always appropriate. That said, the only time you should be triggering atomic operations are on pointer copies and allocations. If you're doing allocations per request, kiss your microsecond response time goodbye -- the reference count overhead is the least of your worries. And you can avoid unnecessary pointer copies by passing shared_ptr<> objects by reference. With a small amount of care, it's possible to reduce shared_ptr<> overhead to essentially zero.
Their polymorphic factory isn't returning "different types." They're both returning A. The derived one is just returning a specific type of A. If the shared "makeOne" returned naked pointer types A* and B* rather than shared_ptr, then this would all work correctly, thanks to C++'s covariant return type support.
Covariant return types are a powerful feature in a language that doesn't support other forms of return type polymorphism. shared_ptrs function so similarly to pointers that it can be surprising when they don't support this behavior. Surprise and capability mismatch with pointers are strikes against shared_ptr. But not sufficient ones to stop me from using them.
No, they're not. One is returning a pointer to A, and the other is returning a pointer to B (which just happens to be a subclass of A). It matters, and just because you can do it with a naked pointer doesn't mean that you should. The problem you've described is a code smell.
If I hand you a pointer to B, you (client code) can do everything that is exposed by B. If I hand you a pointer to A, you can only do the set of things exposed by A. And that's a fundamentally different interface guarantee.
There are cases where covariant return types are abused, so it's fine that you treat it as a code smell. However, C++ does make them necessary for several valid patterns. This does include alexgartrell's example, but here's a popular one:
I have a polymorphic type that should be copyable. Because of the limitations of C++ copy constructors and operators, my only option is to expose a virtual method (call it "copy"). The non-smelly semantics we want are such that if you call "copy" on an object, you get an identical copy of the same type. So if you call "copy" on a pointer of type A, you get an A. Call it on a B, you get a B.
The contract itself is polymorphic. It doesn't commit to returning any particular flavor of A.
We can implement this method if we return naked pointers. But if we want to be safe and return a smart pointer, we have to introduce dynamic casting or other worse smells.
"I have a polymorphic type that should be copyable. Because of the limitations of C++ copy constructors and operators, my only option is to expose a virtual method (call it "copy"). The non-smelly semantics we want are such that if you call "copy" on an object, you get an identical copy of the same type."
Well, yeah...because those semantics are just as smelly as the one you described before. What you describe isn't a copy constructor at all, and trying to make a copy constructor do what you want is a dangerous thing. A "constructor" that takes any subclass of class A and returns an instance of that subclass is inherently brittle: add a new subclass of A, and you've got to update the "constructor" to know about the new type. On top of that, your "constructor" has to introspect into the type to determine what to initialize when, and that's slow.
There are easy ways to get the behavior your want (e.g. make a template function that calls the appropriate class' copy constructor, or -- closest to what you want -- make a template copy constructor function on the class itself), but complaining that you can't make a bog-standard copy constructor do polymorphic construction kind of misses the point.
Here's a better discussion of the problem than I have the space to go into here:
We're going too deep into this, for alexgartrell's relatively simple claim that covariant return types have a reason for being in the language, and that smart pointers omit that capability.
But just to clarify: no base class implementation is aware of derived classes in the copying case, so your smell doesn't exist. See Scott Meyers' "virtual copy constructor" snippet half way down the page:
http://books.google.com/books?id=azvE8V0c-mYC&lpg=PT159&...
You can't do it that neatly without covariant return types. Ergo, you can't do that while returning safe, smart pointers.
I'm tired of marketing boost as "modern C++". Alexei Alexandrescu, the author of "Modern C++ design" book has since gone on greener pastures and got involved with the D language. He participated in the design of D, and wrote a book about it.
Not using shared_ptr a code smell? If you're not able to keep track of your objects yourself, you should not be using C or C++. It's not that hard like you would like us believe.
Also, apart from the cost of maintaining reference counts, shared_ptr makes an extra allocation for the control block. Unless you take care and use make_shared, you end up with allocations that are twice as slow.
On a cynical side: Boost has become Dave Abrahams' business (1) He (and co.) have a vested interest in making you believe that the Boost-way is "the" way of writing "modern" (as defined by HIM and his followers) C++ code. I just wonder whether he had that much foresight, or just took the chance when he saw the opportunity.
(1) I remember once asking about obtaining slides about fusion from a past BoostCon conference and got a blank "no". Slides were available only to the people who paid to attend the conference.
Yes. It's part of the C++ standard. All rants against Dave Abrahams and Boost aside, not using the standard library to do standard things is a code smell.
What "standard things"? shared_ptr is only one of many possible ways to keep track of object lifetimes. Rolling your own reference-counted smart pointer might be a code smell, but not choosing reference counting is far from that.
It doesn't "break"...you just have to think about what you're doing. Which, again, is the point.
If some external code is passing you pointers, then using shared_ptr is exactly the wrong thing to do, because you (presumably) don't own the memory. And if you do own the memory, there's no problem putting it in a shared_ptr. The fact that you can't blindly stuff everything into a shared_ptr is a feature, not a bug.
If your program fits the *_ptr mold (i.e. no unbreakable cycles) and you use it consistently it does tend to be nice. Almost like having automatic memory management.
Anyone care to elaborate on the evils of boost::bind? Maybe from experience?
I used boost::bind and boost::function in VC6 code of all places and I thought that the resulting code turned out to be pretty decent (I think newer compilers let you use slightly less verbose syntax), and I really liked having the functionality of being able to pre-bind certain arguments. For example, I had a menu item click handler that I was able to bind a call to a unary method, but with different parameters. I thought the resulting code was simpler overall (and easier to refactor).
Well IIRC it was a tempting way to replace for-loops with direct list operations ala Python, Ruby etc. But the amount of syntactic noise and cognitive load seemed too much for the added leverage.
Our code had sufficient complexity that I didn't want one more oddity (as many would see it) to explain.
But for a small team, or a team that's all on the same page, maybe it's good.
Sorry to say there's no dramatic story of code explosion here!
I wouldn't use it for for-loops either, but man, boost::bind is so handy when compiled with boost::function! I used to use function pointers and 'void *userData'. If I wanted to pass additional data to the function then I have to allocate a structure and not forgetting to free it later which resulted in a lot of boilerplate code.
shared_ptr - Yes, even non-boosters like this.
regex - pcre
string - stl string
bind - if this is what I think it is, please don't use it. Future maintainers of your code will be grateful.
Having said that, C++ is a large world, and choosing to use these Boost components is perfectly valid.