std::map::find() wrapper

Hi, I've written a small wrapper for std::map::find() (and map-like containers/ranges) that returns a pointer to the value if found (or NULL if not found). See https://svn.boost.org/trac/boost/ticket/5227 The advantage is that you don't need the container type name (for ::reference), you can initialize the pointer inside an if condition, you don't have to compare against ::end() and you don't have to use ->second. For containers of pointers to objects, the wrapper could do an additional dereference. Do you think this would be useful for inclusion in Boost? Greetings, Olaf

20.04.2011 18:54, Olaf van der Spek пишет:
Hi,
I've written a small wrapper for std::map::find() (and map-like containers/ranges) that returns a pointer to the value if found (or NULL if not found).
See https://svn.boost.org/trac/boost/ticket/5227
The advantage is that you don't need the container type name (for ::reference), you can initialize the pointer inside an if condition, you don't have to compare against ::end() and you don't have to use ->second. Hmm... Why not use map::at()?
Compare: if (very_long_type_name_t::mapped_type* i = find_ptr(very_long_name, 1)) cout << *i << "\n"; with: try { cout << very_long_name.at( 1 ) << endl; } catch ( const std::exception& /* exc */ ) { cout << "No value with key 1" << endl; } You don't like exceptions? - Denis

On Wed, Apr 20, 2011 at 9:14 PM, Denis Shevchenko <for.dshevchenko@gmail.com> wrote:
20.04.2011 18:54, Olaf van der Spek пишет:
Hi,
I've written a small wrapper for std::map::find() (and map-like containers/ranges) that returns a pointer to the value if found (or NULL if not found).
See https://svn.boost.org/trac/boost/ticket/5227
The advantage is that you don't need the container type name (for ::reference), you can initialize the pointer inside an if condition, you don't have to compare against ::end() and you don't have to use ->second.
Hmm... Why not use map::at()?
Compare:
if (very_long_type_name_t::mapped_type* i = find_ptr(very_long_name, 1)) cout << *i << "\n";
with:
try { cout << very_long_name.at( 1 ) << endl; } catch ( const std::exception& /* exc */ ) { cout << "No value with key 1" << endl; }
You don't like exceptions?
IMO exceptions are for exceptional cases. Not finding an element is not an exceptional case. Your variant is also a lot longer / more complex, especially if you want to store the returned reference. Olaf

On 04/30/2011 06:55 AM, Olaf van der Spek wrote:
exceptions are for exceptional cases
That oft-used bromide lacks any useful information about when the use of exceptions is appropriate. Exceptions should be used (thrown) when contracts are violated or invariants cannot be maintained. The only time they should not be used in these cases is when checking the invariants would impact performance to such a degree that allowing undefined behavior may be preferable. And in this case, do what map and vector do: provide a checked and unchecked version. A precondition for at() is that the key exists in the map. It is perfectly legitimate for at() to throw an exception for violating its precondition. And /it is perfectly legitimate to make use of this behavior in one's code/. That is, there is nothing wrong with allowing an exception to be thrown if you can afford the stack unwinding. Two bits of code: if (foo.good()) { do_something_with(foo); } and try { do_something_with(foo); } catch (not_good&) {} Both are valid. And sometimes the latter is most appropriate. Personally, I tend to be more averse to functions that return bare pointers. Given the choice of a function that returns a bare pointer which may be NULL or using at() & try/catch, I would probably choose at() first. Then I might try a solution with boost::optional. (I just would prefer that optional::operator*() threw a logic_error for uninitialized values rather than declare its behavior as "undefined".) Rob

On Sat, Apr 30, 2011 at 11:18 PM, Rob Riggs <rob@pangalactic.org> wrote:
On 04/30/2011 06:55 AM, Olaf van der Spek wrote:
exceptions are for exceptional cases
That oft-used bromide lacks any useful information about when the use of exceptions is appropriate.
Exceptions should be used (thrown) when contracts are violated or invariants cannot be maintained. The only time they should not be used in these cases is when checking the invariants would impact performance to such a degree that allowing undefined behavior may be preferable. And in this case, do what map and vector do: provide a checked and unchecked version.
A precondition for at() is that the key exists in the map. It is perfectly legitimate for at() to throw an exception for violating its precondition.
Of course. But I'm looking for a find wrapper. Find doesn't have such a precondition and the wrapper shouldn't have such a precondition either.
And /it is perfectly legitimate to make use of this behavior in one's code/. That is, there is nothing wrong with allowing an exception to be thrown if you can afford the stack unwinding.
Two bits of code:
if (foo.good()) { do_something_with(foo); }
and
try { do_something_with(foo); } catch (not_good&) {}
Both are valid. And sometimes the latter is most appropriate.
Personally, I tend to be more averse to functions that return bare pointers. Given the choice of a function that returns a bare pointer which may be NULL or using at() & try/catch, I would probably choose at() first.
I'd prefer the other way around. Olaf

I'd use such a wrapper that spared me the convoluted iterator incantations. Just check it works for hash_map etc. - Nigel

On Wed, Apr 20, 2011 at 2:54 PM, Olaf van der Spek <ml@vdspek.org> wrote:
Hi,
I've written a small wrapper for std::map::find() (and map-like containers/ranges) that returns a pointer to the value if found (or NULL if not found).
See https://svn.boost.org/trac/boost/ticket/5227
The advantage is that you don't need the container type name (for ::reference), you can initialize the pointer inside an if condition, you don't have to compare against ::end() and you don't have to use ->second.
I'd prefer to see it return a boost::optional<T&> For containers of pointers to objects, the wrapper could do an
additional dereference.
Not a good idea, it's easier to compose than decompose so return a boost::optional<T*&> and the user deref if they want to. - Rob.

On Tue, May 3, 2011 at 6:57 PM, Robert Jones <robertgbjones@gmail.com> wrote:
I'd prefer to see it return a boost::optional<T&>
What's the benefit?
For containers of pointers to objects, the wrapper could do an
additional dereference.
Not a good idea, it's easier to compose than decompose so return a boost::optional<T*&> and the user deref if they want to.
What problem would that avoid? It'd require the user to do an additional check. Olaf

On Tue, May 3, 2011 at 8:52 PM, Olaf van der Spek <ml@vdspek.org> wrote:
On Tue, May 3, 2011 at 6:57 PM, Robert Jones <robertgbjones@gmail.com> wrote:
I'd prefer to see it return a boost::optional<T&>
What's the benefit?
Simply that the difficulty you're addressing is the possibility that there's no value to return. Boost.optional expresses that directly.
For containers of pointers to objects, the wrapper could do an
additional dereference.
Not a good idea, it's easier to compose than decompose so return a boost::optional<T*&> and the user deref if they want to.
What problem would that avoid? It'd require the user to do an additional check.
But you'd lose some flexibility. If you return a reference or copy of the pointed-to object you cannot then change the pointer itself, whereas if you return a reference to the pointer you could. It's also a special case, which is best avoided if possible, that doesn't seem to offer any real benefit. - Rob.

On Wed, May 4, 2011 at 9:37 AM, Robert Jones <robertgbjones@gmail.com> wrote:
On Tue, May 3, 2011 at 8:52 PM, Olaf van der Spek <ml@vdspek.org> wrote:
On Tue, May 3, 2011 at 6:57 PM, Robert Jones <robertgbjones@gmail.com> wrote:
I'd prefer to see it return a boost::optional<T&>
What's the benefit?
Simply that the difficulty you're addressing is the possibility that there's no value to return. Boost.optional expresses that directly.
Returning NULL works just as well. Using a raw pointer also allows you to write string* ptr = find_ptr(). Using Boost Optional would make that longer.
What problem would that avoid? It'd require the user to do an additional check.
But you'd lose some flexibility. If you return a reference or copy of the pointed-to object you cannot then change the pointer itself, whereas if you return a reference to the pointer you could.
That's true, although I'm not sure that's a frequent use-case.
It's also a special case, which is best avoided if possible, that doesn't seem to offer any real benefit.
Containers of pointers occur quite frequently. There's even a Ptr Container lib. Olaf

On Wed, May 4, 2011 at 11:33 AM, Olaf van der Spek <ml@vdspek.org> wrote:
On Tue, May 3, 2011 at 8:52 PM, Olaf van der Spek <ml@vdspek.org> wrote:
On Tue, May 3, 2011 at 6:57 PM, Robert Jones <robertgbjones@gmail.com> wrote:
I'd prefer to see it return a boost::optional<T&>
What's the benefit?
Simply that the difficulty you're addressing is the possibility that
On Wed, May 4, 2011 at 9:37 AM, Robert Jones <robertgbjones@gmail.com> wrote: there's
no value to return. Boost.optional expresses that directly.
Returning NULL works just as well. Using a raw pointer also allows you to write string* ptr = find_ptr(). Using Boost Optional would make that longer.
You'd have to write optional<string> s = find_ptr(); //Now misnamed of course! Either way, at some point you'd have to check the value was usable before using it. - Rob.

On Wed, May 4, 2011 at 12:50 PM, Robert Jones <robertgbjones@gmail.com> wrote:
On Wed, May 4, 2011 at 11:33 AM, Olaf van der Spek <ml@vdspek.org> wrote:
On Tue, May 3, 2011 at 8:52 PM, Olaf van der Spek <ml@vdspek.org> wrote:
On Tue, May 3, 2011 at 6:57 PM, Robert Jones <robertgbjones@gmail.com> wrote:
I'd prefer to see it return a boost::optional<T&>
What's the benefit?
Simply that the difficulty you're addressing is the possibility that
On Wed, May 4, 2011 at 9:37 AM, Robert Jones <robertgbjones@gmail.com> wrote: there's
no value to return. Boost.optional expresses that directly.
Returning NULL works just as well. Using a raw pointer also allows you to write string* ptr = find_ptr(). Using Boost Optional would make that longer.
You'd have to write optional<string> s = find_ptr(); //Now misnamed of course!
boost::optional Shouldn't it be string&?
Either way, at some point you'd have to check the value was usable before using it.
Sure, but that can be done just as well with a pointer. Boost Optional is targeted at functions that return by value. What's the best way to get this wrapper into Boost? Olaf

On Wed, May 4, 2011 at 12:04 PM, Olaf van der Spek <ml@vdspek.org> wrote:
On Wed, May 4, 2011 at 12:50 PM, Robert Jones <robertgbjones@gmail.com> wrote:
On Wed, May 4, 2011 at 11:33 AM, Olaf van der Spek <ml@vdspek.org> wrote:
On Tue, May 3, 2011 at 8:52 PM, Olaf van der Spek <ml@vdspek.org> wrote:
On Tue, May 3, 2011 at 6:57 PM, Robert Jones < robertgbjones@gmail.com> wrote:
I'd prefer to see it return a boost::optional<T&>
What's the benefit?
Simply that the difficulty you're addressing is the possibility that
On Wed, May 4, 2011 at 9:37 AM, Robert Jones <robertgbjones@gmail.com> wrote: there's
no value to return. Boost.optional expresses that directly.
Returning NULL works just as well. Using a raw pointer also allows you to write string* ptr = find_ptr(). Using Boost Optional would make that longer.
You'd have to write optional<string> s = find_ptr(); //Now misnamed of course!
boost::optional Shouldn't it be string&?
Indeed it should.
Either way, at some point you'd have to check the value was usable before using it.
Sure, but that can be done just as well with a pointer.
It can, optional is a generalisation of the no-value value offered by a possibly null pointer. The question is whether it should.
Boost Optional is targeted at functions that return by value.
Is it? I'd always rather thought it targeted at functions that may not necessarily return a value at all.
What's the best way to get this wrapper into Boost?
In detail I don't know. I'm pretty sure you need SVN write access at some point, unless someone applies a patch for you. If it's more than a patch I guess review of your code is required too. - Rob.

On Wed, May 4, 2011 at 2:28 PM, Robert Jones <robertgbjones@gmail.com> wrote:
Boost Optional is targeted at functions that return by value.
Is it? I'd always rather thought it targeted at functions that may not necessarily return a value at all.
Yes, see http://www.boost.org/doc/libs/1_46_1/libs/optional/doc/html/boost_optional/d...
What's the best way to get this wrapper into Boost?
In detail I don't know. I'm pretty sure you need SVN write access at some point, unless someone applies a patch for you. If it's more than a patch I guess review of your code is required too.
Could it be added to the Utility library? If not, to what library could it be added? Olaf

On Wed, May 4, 2011 at 1:04 PM, Olaf van der Spek <ml@vdspek.org> wrote:
On Wed, May 4, 2011 at 12:50 PM, Robert Jones <robertgbjones@gmail.com> wrote:
On Wed, May 4, 2011 at 11:33 AM, Olaf van der Spek <ml@vdspek.org> wrote:
On Tue, May 3, 2011 at 8:52 PM, Olaf van der Spek <ml@vdspek.org> wrote:
On Tue, May 3, 2011 at 6:57 PM, Robert Jones <robertgbjones@gmail.com> wrote:
I'd prefer to see it return a boost::optional<T&>
What's the benefit?
Simply that the difficulty you're addressing is the possibility that
On Wed, May 4, 2011 at 9:37 AM, Robert Jones <robertgbjones@gmail.com> wrote: there's
no value to return. Boost.optional expresses that directly.
Returning NULL works just as well. Using a raw pointer also allows you to write string* ptr = find_ptr(). Using Boost Optional would make that longer.
You'd have to write optional<string> s = find_ptr(); //Now misnamed of course!
boost::optional Shouldn't it be string&?
Either way, at some point you'd have to check the value was usable before using it.
Sure, but that can be done just as well with a pointer. Boost Optional is targeted at functions that return by value.
What's the best way to get this wrapper into Boost?
Somebody? Please? Olaf

On Wed, Jun 1, 2011 at 11:53 AM, Olaf van der Spek <ml@vdspek.org> wrote:
On Wed, May 4, 2011 at 1:04 PM, Olaf van der Spek <ml@vdspek.org> wrote:
What's the best way to get this wrapper into Boost?
Somebody? Please?
Olaf
I don't want to discourage you from finding ways to contribute in the future, but I don't think this particular function has a place in Boost. Judging by the lack of recent replies, I'd assume there's not much interest. -- Cory Nelson

On Wed, Jun 1, 2011 at 11:53, Olaf van der Spek <ml@vdspek.org> wrote:
On Wed, May 4, 2011 at 1:04 PM, Olaf van der Spek <ml@vdspek.org> wrote:
What's the best way to get this wrapper into Boost?
Somebody? Please?
Why do you need it in boost? Putting the 6 lines of code in a file when you need it is no great hardship. Since it's not fundamental functionality, I think the cost of documenting it and making it visible oughtweighs its utility. ~ Scott

On Wed, Jun 1, 2011 at 9:23 PM, Scott McMurray <me22.ca+boost@gmail.com> wrote:
On Wed, Jun 1, 2011 at 11:53, Olaf van der Spek <ml@vdspek.org> wrote:
On Wed, May 4, 2011 at 1:04 PM, Olaf van der Spek <ml@vdspek.org> wrote:
What's the best way to get this wrapper into Boost?
Somebody? Please?
Why do you need it in boost? Putting the 6 lines of code in a file when you need it is no great hardship.
Because you'd like to avoid code duplication. Because not everyone thinks about writing such a wrapper. std::map::find is used quite a lot and in most if not all cases this wrapper would provide a simpler alternative.
Since it's not fundamental functionality, I think the cost of documenting it and making it visible oughtweighs its utility.
Are you the one that's paying that cost? Olaf

On 2 June 2011 12:57, Denis Shevchenko <for.dshevchenko@gmail.com> wrote:
What's the best way to get this wrapper into Boost?
Somebody? Please?
Olaf, the point is not that the library is useful for YOU, but rather whether it useful for OTHERS programmers. This is the criterion for inclusion library in the Boost.
- Denis
We've a function like this among our quite extensive set of useful stl wrapper functions. While I agree this one is useful, I think most organizations are better off collecting their own set of utilities that suits their common use cases perfectly. Writing a std::map::find wrapper that aim to suit everyone is opening a can of worms, IMHO. Personally, I'd like to see a 'Boost.Algorithm.Find' package, that short and concisely can capture the myriads of different ways to express find, and that works on associative and sequence containers of tuples. Maybe something like this. std::vector<int> v; boost::algorithm::find_optional<0>(v, 3) std::vector<std::pair<int, float> > v; boost::algorithm::find_optional<0>(v, 3) boost::algorithm::find_optional<1>(m, 3.0) std::map<int, float> m; boost::algorithm::find_optional<0>(m, 3) -> map::find(...) boost::algorithm::find_optional<1>(m, 3.0) -> std::find(...) The compile time constant indicates which tuple element to find (fusion compatible, say). Return value is always optional<Container::value_type&>. (and const versions thereof) The algorithm is responsible for using the best implementation of find, i.e. avoid going trough std::find() if the container is associative and the element index is 0. If element index is 1, it needs to use std::find also for associative, Unless it's boost::bimap.. =) Probably not worth the effort. - Christian

On Thu, Jun 2, 2011 at 7:57 PM, Denis Shevchenko <for.dshevchenko@gmail.com> wrote:
What's the best way to get this wrapper into Boost?
Somebody? Please?
Olaf, the point is not that the library is useful for YOU, but rather whether it useful for OTHERS programmers. This is the criterion for inclusion library in the Boost.
I know. See my previous reply.

On 05/04/2011 04:33 AM, Olaf van der Spek wrote:
It's also a special case, which is best avoided if possible, that doesn't
seem to offer any real benefit. Containers of pointers occur quite frequently. There's even a Ptr Container lib.
Those containers manage the lifetimes of heap allocated objects just as smart pointers manage the lifetimes of theirs. One of the hallmarks of modern C++ programming is the absence of bare pointers. We have iterators, smart pointers and pointer containers to manage and hide pointers for us. We have a healthy distrust of bare pointers because they are so easily misused. boost::optional solves the problem without introducing bare pointers. In fact, optional solves the problem specifically so we can avoid using bare pointers. Rob

On Wed, May 4, 2011 at 2:37 PM, Rob Riggs <rob@pangalactic.org> wrote:
On 05/04/2011 04:33 AM, Olaf van der Spek wrote:
It's also a special case, which is best avoided if possible, that doesn't
seem to offer any real benefit.
Containers of pointers occur quite frequently. There's even a Ptr Container lib.
Those containers manage the lifetimes of heap allocated objects just as smart pointers manage the lifetimes of theirs.
One of the hallmarks of modern C++ programming is the absence of bare pointers. We have iterators, smart pointers and pointer containers to manage and hide pointers for us. We have a healthy distrust of bare pointers because they are so easily misused.
I'm not sure what your code looks like, but IMO bare pointers are perfectly fine in certain situations. Like this one, where no ownership issues are present. Olaf

Olaf van der Spek wrote:
On Wed, May 4, 2011 at 2:37 PM, Rob Riggs <rob@pangalactic.org> wrote:
One of the hallmarks of modern C++ programming is the absence of bare pointers. We have iterators, smart pointers and pointer containers to manage and hide pointers for us. We have a healthy distrust of bare pointers because they are so easily misused.
I'm not sure what your code looks like, but IMO bare pointers are perfectly fine in certain situations. Like this one, where no ownership issues are present.
A function that returns a pointer immediately begs the question of ownership. It is perfectly reasonable to think that one should call delete on such a pointer when finished with it. That, of course, would have nasty consequences, but it is the result of returning a pointer. Returning a boost::optional with a reference to the element conveys two things. First, the reference clearly indicates that the container maintains ownership. Second, there may be no value. Whether the limitations of optional when using reference types is a problem in this context, I'll leave to you (see <http://www.boost.org/doc/libs/1_46_1/libs/optional/doc/html/boost_optional/optional_references.html>). Local conventions may dictate that all ownership transfers are done via std::auto_ptr, for example, and never through raw pointers, leaving raw pointers as non-owning, possibly null references. Boost, however, is used in various places with different conventions. _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

On Wed, May 4, 2011 at 3:08 PM, Stewart, Robert <Robert.Stewart@sig.com> wrote:
Olaf van der Spek wrote:
On Wed, May 4, 2011 at 2:37 PM, Rob Riggs <rob@pangalactic.org> wrote:
One of the hallmarks of modern C++ programming is the absence of bare pointers. We have iterators, smart pointers and pointer containers to manage and hide pointers for us. We have a healthy distrust of bare pointers because they are so easily misused.
I'm not sure what your code looks like, but IMO bare pointers are perfectly fine in certain situations. Like this one, where no ownership issues are present.
A function that returns a pointer immediately begs the question of ownership. It is perfectly reasonable to think that one should call delete on such a pointer when finished with it. That, of course, would have nasty consequences, but it is the result of returning a pointer.
Is it? I've no idea why that would be reasonable. You don't delete something just because you can.
Returning a boost::optional with a reference to the element conveys two things. First, the reference clearly indicates that the container maintains ownership. Second, there may be no value. Whether the limitations of optional when using reference types is a problem in this context, I'll leave to you (see <http://www.boost.org/doc/libs/1_46_1/libs/optional/doc/html/boost_optional/optional_references.html>).
Providing a variant that returns boost::optional (find_opt / find_optional) is of course possible.
Local conventions may dictate that all ownership transfers are done via std::auto_ptr, for example, and never through raw pointers, leaving raw pointers as non-owning, possibly null references. Boost, however, is used in various places with different conventions.
You can't expect every single lib to follow your local convention, can you? Olaf

Olaf van der Spek wrote:
On Wed, May 4, 2011 at 3:08 PM, Stewart, Robert <Robert.Stewart@sig.com> wrote:
Olaf van der Spek wrote:
I'm not sure what your code looks like, but IMO bare pointers are perfectly fine in certain situations. Like this one, where no ownership issues are present.
A function that returns a pointer immediately begs the question of ownership. It is perfectly reasonable to think that one should call delete on such a pointer when finished with it. That, of course, would have nasty consequences, but it is the result of returning a pointer.
Is it? I've no idea why that would be reasonable. You don't delete something just because you can.
It is common for those without sufficient knowledge of smart pointers to return raw pointers from a functions that allocate the memory. Such programmers would suspect the calling code owns the memory referenced by the raw pointer. Thus, such an interface can be a source of errors. That a person can so readily delete the return value with the attendant ill effects on the container make the interface problematic. An interface that returns a reference doesn't have the same problem. The caller must take the address and then delete it, which requires conscious misuse.
Local conventions may dictate that all ownership transfers are done via std::auto_ptr, for example, and never through raw pointers, leaving raw pointers as non-owning, possibly null references. Boost, however, is used in various places with different conventions.
You can't expect every single lib to follow your local convention, can you?
No. That was my point. Boost cannot assume local conventions to give proper meaning to a function returning a raw pointer and so should avoid doing so. _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

On 4 May 2011 13:24, Stewart, Robert <Robert.Stewart@sig.com> wrote:
An interface that returns a reference doesn't have the same problem. The caller must take the address and then delete it, which requires conscious misuse.
So: delete foo; is not conscious misuse, but delete &foo; is conscious misuse? Really? I would say both are conscious misuse, since both require the user to do explicitly write code to do something. No. That was my point. Boost cannot assume local conventions to give
proper meaning to a function returning a raw pointer and so should avoid doing so.
I think Boost can assume that the user isn't going to do random things with a pointer returned by an API. That being said, we are better off returning a type which only allows desired operations. -- Nevin ":-)" Liber <mailto:nevin@eviloverlord.com> (847) 691-1404

Nevin Liber wrote:
On 4 May 2011 13:24, Stewart, Robert <Robert.Stewart@sig.com> wrote:
An interface that returns a reference doesn't have the same problem. The caller must take the address and then delete it, which requires conscious misuse.
So:
delete foo;
is not conscious misuse, but
delete &foo;
is conscious misuse? Really? I would say both are conscious misuse, since both require the user to do explicitly write code to do something.
Not quite. foo * f(); foo & g(); void h() { foo * p = f(); delete p; // not unreasonable foo & r = g(); delete &r; // misuse } g()'s interface returns a reference and not a pointer. That is a clear indication that g()'s caller does not own the memory. By contrast, f() returns a pointer. The caller is not unreasonable in thinking ownership might be transferred, particularly if local conventions indicate as much. An interface that precludes the possibility for that sort of confusion is better. _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

On Wed, May 4, 2011 at 10:14 PM, Stewart, Robert <Robert.Stewart@sig.com> wrote:
void h() { foo * p = f(); delete p; // not unreasonable foo & r = g(); delete &r; // misuse }
g()'s interface returns a reference and not a pointer. That is a clear indication that g()'s caller does not own the memory. By contrast, f() returns a pointer. The caller is not unreasonable in thinking ownership might be transferred, particularly if local conventions indicate as much.
An interface that precludes the possibility for that sort of confusion is better.
You're ignoring the fact that such an interface makes normale usage more complex. Also, the difference between delete p and delete &r is very small, if the user does not understand how ownership works he might just write delete &r too. I don't get why you think it's reasonable to assume ownership is transfered by a find function. Olaf

On 4 May 2011 15:14, Stewart, Robert <Robert.Stewart@sig.com> wrote:
g()'s interface returns a reference and not a pointer. That is a clear indication that g()'s caller does not own the memory. By contrast, f() returns a pointer. The caller is not unreasonable in thinking ownership might be transferred, particularly if local conventions indicate as much.
I have a hard time imaging a useable code base which has a convention that any time a pointer is returned one should assume that the memory must be deleted by the caller. You couldn't even call something as simple as strncat in such an environment, let alone most libraries with a C API. -- Nevin ":-)" Liber <mailto:nevin@eviloverlord.com> (847) 691-1404

On 05/04/2011 04:27 PM, Nevin Liber wrote:
I have a hard time imaging a useable code base which has a convention that any time a pointer is returned one should assume that the memory must be deleted by the caller.
I remember reading an article in Dr. Dobbs years ago where one of the regular writers was reviewing some framework library. His opinion was the opposite of yours I think. He didn't like that the framework was handling the lifetime for him, even of its own objects. He really wanted to see a 'delete' in his own code matching every place an object was created dynamically. This was before smart pointers caught on.
You couldn't even call something as simple as strncat in such an environment, let alone most libraries with a C API.
Haha, most of the time programmers can't call strncat without writing a nul-termination bug anyway. I don't think I've called strncat in my last few hundred thousand lines of code. I do have to work with C APIs all the time though and, you're right, it takes *a lot* of extra care to do it correctly. - Marsh

Nevin Liber wrote:
I have a hard time imaging a useable code base which has a convention that any time a pointer is returned one should assume that the memory must be deleted by the caller.
That used to be the norm before smart pointers caught on. I cannot say how common it is today, but the possibility exists.
You couldn't even call something as simple as strncat in such an environment, let alone most libraries with a C API.
Such developers always made a distinction between C++ code and the C RTL. _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

On Thu, May 5, 2011 at 2:01 PM, Stewart, Robert <Robert.Stewart@sig.com> wrote:
Nevin Liber wrote:
I have a hard time imaging a useable code base which has a convention that any time a pointer is returned one should assume that the memory must be deleted by the caller.
That used to be the norm before smart pointers caught on. I cannot say how common it is today, but the possibility exists.
Really? Got any reference for that?

Olaf van der Spek wrote:
On Thu, May 5, 2011 at 2:01 PM, Stewart, Robert <Robert.Stewart@sig.com> wrote:
Nevin Liber wrote:
I have a hard time imaging a useable code base which has a convention that any time a pointer is returned one should assume that the memory must be deleted by the caller.
That used to be the norm before smart pointers caught on. I cannot say how common it is today, but the possibility exists.
Really? Got any reference for that?
Experience. _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

I'm not sure what your code looks like, but IMO bare pointers are perfectly fine in certain situations. Like this one, where no ownership issues are present. IMHO, if program *REQUIRES* bare pointers - this program, probably, should be refactored.
For example, I don't use bare pointers in my code. Never (except operator new() result). I write system-oriented programs and libraries (network, protocols, database, etc.), but I never need bare pointers. - Denis

On 4 May 2011 10:44, Denis Shevchenko <for.dshevchenko@gmail.com> wrote:
I'm not sure what your code looks like, but IMO bare pointers are
perfectly fine in certain situations. Like this one, where no ownership issues are present.
IMHO, if program *REQUIRES* bare pointers - this program, probably, should be refactored.
If it isn't involved with ownership, what is wrong with raw pointers? I find it far more flexible to separate the concern of ownership from usage.
For example, I don't use bare pointers in my code. Never (except operator new() result). I write system-oriented programs and libraries (network, protocols, database, etc.), but I never need bare pointers.
What you do is far less interesting to me than why you do it. Is it just dogma, or is there some underlying reason? -- Nevin ":-)" Liber <mailto:nevin@eviloverlord.com> (847) 691-1404

On Wed, May 4, 2011 at 7:51 PM, Denis Shevchenko <for.dshevchenko@gmail.com> wrote:
What you do is far less interesting to me than why you do it. Is it just dogma, or is there some underlying reason?
No, it's not dogma. Raw pointers are not the universal evil, but I never use raw pointers because I *can* write my code without raw pointers. :-)
That still doesn't tell me why that's better in this case. Olaf

On 05/04/2011 01:12 PM, Olaf van der Spek wrote:
On Wed, May 4, 2011 at 7:51 PM, Denis Shevchenko <for.dshevchenko@gmail.com> wrote:
No, it's not dogma. Raw pointers are not the universal evil, but I never use raw pointers because I *can* write my code without raw pointers. :-)
That still doesn't tell me why that's better in this case.
Because it uses the C++ type system to restrict the number of possible interpretations about the valid operations on the returned value, thus communicating information that would otherwise go unspecified. Sometimes this lets the compiler help spot problems with the program's correctness. Even if not now, it can once in a while save your ass as the program evolves over time. - Marsh

On 4 May 2011 13:42, Marsh Ray <marsh@extendedsubset.com> wrote:
On 05/04/2011 01:12 PM, Olaf van der Spek wrote:
On Wed, May 4, 2011 at 7:51 PM, Denis Shevchenko <for.dshevchenko@gmail.com> wrote:
No, it's not dogma. Raw pointers are not the universal evil, but I never use raw pointers because I *can* write my code without raw pointers. :-)
That still doesn't tell me why that's better in this case.
Because it uses the C++ type system to restrict the number of possible interpretations about the valid operations on the returned value, thus communicating information that would otherwise go unspecified.
If, for instance, you pass a shared_ptr, you are saying that the callee is allowed to be involved with ownership. This is functionality I typically don't want to grant willy-nilly to every function I call. If you want to use the C++ type system to enforce this kind of thing, create new types to wrap the raw pointers; don't reuse things like shared_ptr for this. (And if you are creating new types, then good for you, and I remove my objection. But it isn't typical, at least in my experience.)
Sometimes this lets the compiler help spot problems with the program's correctness.
Could you elaborate? Again, we are talking about pointers not involved with ownership. -- Nevin ":-)" Liber <mailto:nevin@eviloverlord.com> (847) 691-1404

On 05/04/2011 02:14 PM, Nevin Liber wrote:
On 4 May 2011 13:42, Marsh Ray<marsh@extendedsubset.com> wrote:
Because it uses the C++ type system to restrict the number of possible interpretations about the valid operations on the returned value, thus communicating information that would otherwise go unspecified.
If, for instance, you pass a shared_ptr, you are saying that the callee is allowed to be involved with ownership.
We could say that the interface is defined in such a way that the programming language does not prohibit it, even though the interface could have been defined that way. But that's still not the same as saying it's allowed. Consider intrusive_ptr. In 'intruded pointeee' classes it's ususally possible to construct a valid intrusive_ptr from just a raw pointer. Following your maximally-allowed logic, it would be impossible to refer by any method to an 'intruded pointeee' class instance without the callee being "involved with ownership". Even an object constructed on the stack.
This is functionality I typically don't want to grant willy-nilly to every function I call.
Then don't pass them as shared_ptrs. If you know it's non-null, deref it and pass a reference, otherwise pass an optional<>.
If you want to use the C++ type system to enforce this kind of thing, create new types to wrap the raw pointers; don't reuse things like shared_ptr for this.
Agreed.
(And if you are creating new types, then good for you, and I remove my objection. But it isn't typical, at least in my experience.)
Look again... how many variations on smart pointers and reference wrapper have been invented over the years? I put together one when I needed it the other day based on the "clone_ptrs" designs that others have proposed.
Sometimes this lets the compiler help spot problems with the program's correctness.
Could you elaborate? Again, we are talking about pointers not involved with ownership.
OK. So to the C++ compiler, pointers are pointers very much like those in C. There's no concept of some pointers "involved with ownership" and others "not involved with ownership". These terms were initially coined by developers in order to talk about why they had so many memory leaks and use-after-free bugs in their program. These terms are great in source code comments when you have to use pointers. Then people realized that C++ had templates and deterministic destructors and could support this nifty technique RAII which could use the familiar scoped lifetime rules to help manage "ownership" and lifetime issues of dynamically allocated objects. So we got smart pointers particularly to help with that one aspect of program correctness. But C++ has a much more powerful statically-checked type system. It supports abstraction and provides a logic programming system based on template instantiation. This has the ability to do a great many useful things, but most of all it assists us in proving the correctness of our programs. http://en.wikipedia.org/wiki/Curry%E2%80%93Howard_correspondence However, it can only be effective to the degree to which we use it, which primarily means defining _restrictions_ on the expressions in which objects of types are allowed to participate. Interestingly, this is in some ways the exact opposite of what the OO paradigm does with behavioral subtyping through inheritance. The Liskov substitution principle states "if S is a subtype of T, then objects of type T may be replaced with objects of type S" (wikipedia). Note that that's a positive statement. Consider the 'const' qualifier for example. A 'const T' is not a behavioral subtype of T because you can't use const Ts in many places where a T is required. If you tried to make T a subtype of 'const T', then the subtype could no longer implement the behavior that it always stays the same. Quite often, the concepts people want to express about types do not fit cleanly into either box. The valid actions for one type are often a merely overlapping set with of those of another. Sorting all this out gets maddeningly complicated and most other languages seem to have given up and imposed severe restrictions on multiple inheritance and their template system. Still, most of us find the ability to define restrictions on our types very useful and the zeal with which it is pursued is one of the defining characteristics of the Boost project (and C++ in general). People wrote code without regard to 'const' for a long time. It wasn't an effortless transition, but now that the dust has settled we try to write const-correct code whenever practical. Again, the general principle is that we should refine the types we use in our programs as precisely as practical. They should be abstract where possible in support of generic programming and restrictive in support of compile-time correctness checking. Of course, this often introduces additional complexity and the trade-off is best determined by the needs of the situation and the developer's good taste. Off the top of my head, here are some things that pointers can do that optional<T> or optional<&T> can not: pointer arithmetic operator [] exist in an undefined not-default-constructed state operator delete (i.e., raise questions of ownership) conversion to base * and void * types reinterpretation as uintptr_t passing to extern "C" API functions I don't see anything that optional<> can do that a pointer cannot do, except perhaps some methods of explicit construction that are probably more efficient than their equivalents for pointers (efficiency is often a good by-product of the type information too). Thus, it is better to use optional<T or &> instead of pointers whenever it supplies the required operations. - Marsh

On Thu, May 5, 2011 at 1:01 AM, Marsh Ray <marsh@extendedsubset.com> wrote:
Thus, it is better to use optional<T or &> instead of pointers whenever it supplies the required operations.
When auto is available, the disadvantage of boost::optional is minimal. It's clear that some people really prefer boost::optional, so find_opt or find_optional could be provided as well. But again: To what Boost lib could this be added? Utility? Olaf

Den 05-05-2011 11:00, Olaf van der Spek skrev:
On Thu, May 5, 2011 at 1:01 AM, Marsh Ray<marsh@extendedsubset.com> wrote:
Thus, it is better to use optional<T or&> instead of pointers whenever it supplies the required operations.
When auto is available, the disadvantage of boost::optional is minimal. It's clear that some people really prefer boost::optional, so find_opt or find_optional could be provided as well.
T* has the distict advantage that it is optimally efficient, even though that optional<T&> might slightly be more type-safe. Some dislike "naked" pointers in interfaces. I think a good guide line is that such pointer is always without ownership, and so T* foo() is quite easy to read IMO. No need for another overload. We might as well take advantage of the fact that pointers are "optional" by default. There is no need to complicate things. -Thorsten

On Thu, May 5, 2011 at 11:52 AM, Thorsten Ottosen < thorsten.ottosen@dezide.com> wrote:
Den 05-05-2011 11:00, Olaf van der Spek skrev:
On Thu, May 5, 2011 at 1:01 AM, Marsh Ray<marsh@extendedsubset.com>
wrote:
Thus, it is better to use optional<T or&> instead of pointers whenever it supplies the required operations.
When auto is available, the disadvantage of boost::optional is minimal. It's clear that some people really prefer boost::optional, so find_opt or find_optional could be provided as well.
T* has the distict advantage that it is optimally efficient, even though that optional<T&> might slightly be more type-safe.
Some dislike "naked" pointers in interfaces. I think a good guide line is that such pointer is always without ownership, and so
T* foo()
"Some" would be me. A good guideline is always to try to raise the abstraction level of our programming - Boost.optional seems to me to do that. The guide you suggest may be a good guide, but it would be better to avoid the need for a guide at all - if you can say it in code, say it in code.
is quite easy to read IMO. No need for another overload. We might as well take advantage of the fact that pointers are "optional" by default. There is no need to complicate things.
Surely this could be used as an argument for pointers instead of optionals for every use of optional. Efficiency taken to this extent seems like the wrong choice - surely we should write for clarity first and efficiency second, iff measurement suggests it's a problem. - Rob.

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On Thursday, May 05, 2011, Robert Jones wrote:
On Thu, May 5, 2011 at 11:52 AM, Thorsten Ottosen <
is quite easy to read IMO. No need for another overload. We might as well take advantage of the fact that pointers are "optional" by default. There is no need to complicate things.
Surely this could be used as an argument for pointers instead of optionals for every use of optional.
I see it as only an argument for usage of pointers over optional references, since optionals copy their template argument type by value and are thus generally quite un-pointer like. Personally, I don't think I've ever bothered using an optional reference (but I have made use of optionals). -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.10 (GNU/Linux) iEYEARECAAYFAk3CoQUACgkQ5vihyNWuA4W2vACffSImLEj4SPOn7wYJOUPK5ktm vvAAoKZ0IQ8mEcjLA9EkeoPaVnbxCt9C =CV8p -----END PGP SIGNATURE-----

On Thu, May 5, 2011 at 1:57 PM, Robert Jones <robertgbjones@gmail.com> wrote:
Surely this could be used as an argument for pointers instead of optionals for every use of optional. Efficiency
No it can't. Boost Optional is necessary (and designed) for optional values returned by value.
taken to this extent seems like the wrong choice - surely we should write for clarity first and efficiency second, iff measurement suggests it's a problem.
Olaf

Thorsten Ottosen wrote:
Some dislike "naked" pointers in interfaces. I think a good guide line is that such pointer is always without ownership, and so
T* foo()
is quite easy to read IMO. No need for another overload.
That is a viewpoint you cannot impose on library users, however. _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

Den 05-05-2011 13:59, Stewart, Robert skrev:
Thorsten Ottosen wrote:
Some dislike "naked" pointers in interfaces. I think a good guide line is that such pointer is always without ownership, and so
T* foo()
is quite easy to read IMO. No need for another overload.
That is a viewpoint you cannot impose on library users, however.
Well, how does the library user knows that an optional<T&> does not own the stored reference? Can I say delete &*find_some_thing() ? To be sure, I must consult the documentation. Just by looking at the name of the function find_some_thing() it would be highly surprising if that involved transfer of ownership of a resource. Don't get me wrong: I'm a great fan of optional<T>, but that does not imply that you can't use it too much. -Thorsten

Den 05-05-2011 15:49, Sebastian Redl skrev:
On 05.05.2011 15:25, Thorsten Ottosen wrote:
Well, how does the library user knows that an optional<T&> does not own the stored reference?
C++ references are *never* owning. Have never been, except in really outlandish designs.
Maybe we need a specialization boost::optional<T*> that does not store a boolean flag (and otherwise behave like optional<T&>? Or a new dummy smart pointer to signify that the held ptr object is owned by another object. E.g. boost::ptr<T> ? -Thorsten

On 05/05/2011 09:34 AM, Thorsten Ottosen wrote:
Maybe we need a specialization boost::optional<T*> that does not store a boolean flag (and otherwise behave like optional<T&>?
That would change the semantics of the existing type.
Or a new dummy smart pointer to signify that the held ptr object is owned by another object. E.g. boost::ptr<T> ?
I like it! boost::dumb_and_weak_pointer<T> ? You could do it today with shared_ptr and a custom no-op deleter, or more efficiently with intrusive_ptr and no-op implementations of intrusive_ptr_(add_ref|release). - Marsh

On 05/05/2011 05:52 AM, Thorsten Ottosen wrote:
T* has the distict advantage that it is optimally efficient, even though that optional<T&> might slightly be more type-safe.
sizeof(T*) is guaranteed to be nonzero, and in practice is 4 or 8 bytes, whereas references are allowed to have no size at all. References have a very restricted set of allowed behaviors. Pointers are first-class values in their own right and this makes it impossible (in the general case) for a compiler to know if an object is being aliased by other pointers. You make the optimizer's job much, much harder by taking the address of something. It depends on a lot of factors of course, but there are cases where references are faster than pointers, so T* is not necessarily "optimally efficient". - Marsh

Den 05-05-2011 16:48, Marsh Ray skrev:
On 05/05/2011 05:52 AM, Thorsten Ottosen wrote:
T* has the distict advantage that it is optimally efficient, even though that optional<T&> might slightly be more type-safe.
sizeof(T*) is guaranteed to be nonzero, and in practice is 4 or 8 bytes, whereas references are allowed to have no size at all.
Can you mention just one ABI where references are merely pointers?
References have a very restricted set of allowed behaviors. Pointers are first-class values in their own right and this makes it impossible (in the general case) for a compiler to know if an object is being aliased by other pointers. You make the optimizer's job much, much harder by taking the address of something. It depends on a lot of factors of course, but there are cases where references are faster than pointers, so T* is not necessarily "optimally efficient".
perhaps, but in this case the reference /is/ a dereferenced pointer from within a map. -Thorsten

Den 05-05-2011 16:53, Thorsten Ottosen skrev:
Den 05-05-2011 16:48, Marsh Ray skrev:
On 05/05/2011 05:52 AM, Thorsten Ottosen wrote:
T* has the distict advantage that it is optimally efficient, even though that optional<T&> might slightly be more type-safe.
sizeof(T*) is guaranteed to be nonzero, and in practice is 4 or 8 bytes, whereas references are allowed to have no size at all.
Can you mention just one ABI where references are merely pointers?
... not merely ... -Thorsten

On 05/05/2011 10:17 AM, Thorsten Ottosen wrote:
Den 05-05-2011 16:53, Thorsten Ottosen skrev:
Den 05-05-2011 16:48, Marsh Ray skrev:
sizeof(T*) is guaranteed to be nonzero, and in practice is 4 or 8 bytes, whereas references are allowed to have no size at all.
Can you mention just one ABI where references are merely pointers?
... not merely ...
So I'm interpreting that as "Can you give an example of an actual C++ program where the compiler does not emit the same code for references as the analogous code using plain pointers." ... I played around with g++-4.6.0 and was not able to make it emit different code in the short time I'm willing to try. G++ seems to miss some opportunities for optimization of references, and seems to be smart about tracking pointer values. - Marsh

On 05/05/11 19:35, Marsh Ray wrote: <snip>
So I'm interpreting that as "Can you give an example of an actual C++ program where the compiler does not emit the same code for references as the analogous code using plain pointers."
.... I played around with g++-4.6.0 and was not able to make it emit different code in the short time I'm willing to try. G++ seems to miss some opportunities for optimization of references, and seems to be smart about tracking pointer values.
There's this: struct A { int x; }; struct B { int y; }; struct C : A, B {}; C& f(B& b) { return static_cast<C&>(b); } C* g(B* b) { return static_cast<C*>(b); } which compiles to: 0000000000000000 <_Z1fR1B>: 0: 48 8d 47 fc lea -0x4(%rdi),%rax 4: c3 retq 0000000000000010 <_Z1gP1B>: 10: 48 8d 57 fc lea -0x4(%rdi),%rdx 14: 31 c0 xor %eax,%eax 16: 48 85 ff test %rdi,%rdi 19: 48 0f 45 c2 cmovne %rdx,%rax 1d: c3 retq The compiler doesn't have to do a null-check for the reference case, so it's shorter. John Bytheway

Message du 05/05/11 23:50 De : "John Bytheway" A : boost@lists.boost.org Copie à : Objet : Re: [boost] std::map::find() wrapper
On 05/05/11 19:35, Marsh Ray wrote:
So I'm interpreting that as "Can you give an example of an actual C++ program where the compiler does not emit the same code for references as the analogous code using plain pointers."
.... I played around with g++-4.6.0 and was not able to make it emit different code in the short time I'm willing to try. G++ seems to miss some opportunities for optimization of references, and seems to be smart about tracking pointer values.
There's this:
struct A { int x; }; struct B { int y; }; struct C : A, B {};
C& f(B& b) { return static_cast(b); }
C* g(B* b) { return static_cast(b); }
which compiles to:
0000000000000000 <_Z1fR1B>: 0: 48 8d 47 fc lea -0x4(%rdi),%rax 4: c3 retq
0000000000000010 <_Z1gP1B>: 10: 48 8d 57 fc lea -0x4(%rdi),%rdx 14: 31 c0 xor %eax,%eax 16: 48 85 ff test %rdi,%rdi 19: 48 0f 45 c2 cmovne %rdx,%rax 1d: c3 retq
The compiler doesn't have to do a null-check for the reference case, so it's shorter.
I would be interested in knowing why the compiler need to check for 0 when you apply a static_cast? Best, Vicente

On 05/05/11 22:55, Vicente BOTET wrote:
Message du 05/05/11 23:50 De : "John Bytheway" A : boost@lists.boost.org Copie à : Objet : Re: [boost] std::map::find() wrapper
<snip>
There's this:
struct A { int x; }; struct B { int y; }; struct C : A, B {};
C& f(B& b) { return static_cast(b); }
C* g(B* b) { return static_cast(b); }
which compiles to:
0000000000000000 <_Z1fR1B>: 0: 48 8d 47 fc lea -0x4(%rdi),%rax 4: c3 retq
0000000000000010 <_Z1gP1B>: 10: 48 8d 57 fc lea -0x4(%rdi),%rdx 14: 31 c0 xor %eax,%eax 16: 48 85 ff test %rdi,%rdi 19: 48 0f 45 c2 cmovne %rdx,%rax 1d: c3 retq
The compiler doesn't have to do a null-check for the reference case, so it's shorter.
I would be interested in knowing why the compiler need to check for 0 when you apply a static_cast?
Because static_cast<C*>(b) is valid when b == 0, and must return 0, but any non-zero value must be changed (by subtracting 4) because of the multiple inheritance. This is a pretty obscure corner case, but it does demonstrate that the non-NULL guarantee for references has at least some performance implications. John Bytheway

On 05/05/2011 03:06 PM, John Bytheway wrote: [snip]
Because static_cast<C*>(b) is valid when b == 0, and must return 0, but any non-zero value must be changed (by subtracting 4) because of the multiple inheritance. This is a pretty obscure corner case, but it does demonstrate that the non-NULL guarantee for references has at least some performance implications.
I think it is fairly obvious that knowing a pointer to be non-null can be helpful to the optimizer. However, this example does not seem relevant to the discussion of the find function, since the whole point is that the pointer returned may indeed be null and therefore should be checked. I imagine you'll find with most compilers that code like: C *c = find(...); if (c) { B *b = static_cast<B*>(c); // do something with b } will result in only a single null pointer check.

Message du 06/05/11 00:07 De : "John Bytheway" A : boost@lists.boost.org Copie à : Objet : Re: [boost] std::map::find() wrapper
On 05/05/11 22:55, Vicente BOTET wrote:
Message du 05/05/11 23:50 De : "John Bytheway" A : boost@lists.boost.org Copie à : Objet : Re: [boost] std::map::find() wrapper
The compiler doesn't have to do a null-check for the reference case, so it's shorter.
I would be interested in knowing why the compiler need to check for 0 when you apply a static_cast?
Because static_cast(b) is valid when b == 0, and must return 0, but any non-zero value must be changed (by subtracting 4) because of the multiple inheritance. This is a pretty obscure corner case, but it does demonstrate that the non-NULL guarantee for references has at least some performance implications.
Thanks, I miss the multiple inheritance. Best, Vicente

On Thu, May 5, 2011 at 11:00 AM, Olaf van der Spek <ml@vdspek.org> wrote:
On Thu, May 5, 2011 at 1:01 AM, Marsh Ray <marsh@extendedsubset.com> wrote:
Thus, it is better to use optional<T or &> instead of pointers whenever it supplies the required operations.
When auto is available, the disadvantage of boost::optional is minimal. It's clear that some people really prefer boost::optional, so find_opt or find_optional could be provided as well.
But again: To what Boost lib could this be added? Utility?
Somebody? Olaf
participants (17)
-
Christian Holmquist
-
Cory Nelson
-
Denis Shevchenko
-
Frank Mori Hess
-
Jeremy Maitin-Shepard
-
John Bytheway
-
Marsh Ray
-
Nevin Liber
-
Nigel Stewart
-
Olaf van der Spek
-
Rob Riggs
-
Robert Jones
-
Scott McMurray
-
Sebastian Redl
-
Stewart, Robert
-
Thorsten Ottosen
-
Vicente BOTET