Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Consider this simple code to swap the first two items of a vector:

    vector<T> foo = ...
    auto tmp = foo[0];
    foo[0] = foo[1];
    foo[1] = tmp;
When T is int, it swaps as expected. But when T is bool, instead of swapping, it copies the second item over the first, with nary a warning. And I'll wager you could stare at that code all day without spotting the bug.

So yes, be afraid.



Actually, you did remind me that I did run into a type inference bug: the original ScopeGuard implementation ( http://www.drdobbs.com/cpp/generic-change-the-way-you-write-... ) relies on binding a temporary object to a const reference to guarantee that the destructor doesn't get elided. So changing ScopeGuard foo = MakeGuard(...) to auto foo = MakeGuard(...) does change the meaning of the code, and you may have the cleanup code optimized away.

An updated version of ScopeGuard doesn't have this problem ( https://github.com/facebook/folly/blob/master/folly/ScopeGua... ).

So, yes, I'll concede that code that expects you to cast some kind of proxy type to a different type (e.g., the vector<bool> example, or the original ScopeGuard, or perhaps valarray) isn't ready for type inference. But that kind of code is pretty rare, so the list of exceptions to the rule should be short.

Besides, if you want to swap two elements, use std::swap.


Correction: given a vector<bool> foo, "std::swap(foo[0], foo[1])" won't compile because std::swap takes parameters as non-const references, i.e., not temporaries (given a vector<int> bar, "std::swap(bar[0], bar[1])" won't compile either); so you have to use "std::iter_swap(foo.begin(), foo.begin + 1)" to (correctly) swap the first two elements.


And another correction: given a vector<int> bar, "std::swap(bar[0], bar[1])" does compile fine. All vectors other than vector<bool> return modifiable references when elements are accessed with square brackets.

I'll stop it now.


I object to calling vector<bool> a vector. I'm afraid of it, not of auto.


This is a contrived example. Not only is vector<bool> the real problem, but this is a standout terrible way to swap elements, especially in generic code.


You have to admit, it's hilarious that the most natural swap implementation you could possibly write (and the one used in C++03) is now "standout terrible." It's not wrong to point that out, but it is wrong to assign blame to the hapless programmer instead of the language.

And even if we make it not-generic, and add some C++11:

    std::vector<bool> foo = ...;
    auto tmp = std::move(foo[0]);
    foo[0] = std::move(foo[1]);
    foo[1] = std::move(tmp);
 
We've fixed nothing. The problem remains!

(It works if you use std::swap, I think because whatever type tmp resolves to happens to overload std::swap to do the right thing.)

So why is vector<bool> the "real problem?" It's because its operator[] doesn't return a bool&, but instead some type "convertible to bool," that may do lots of other stuff too. But the standard is chock-full of language like that. For example, with iterators: `str.begin() != str.end()` Is that a bool? `str.begin()[2]`. Is that a char? The standard doesn't require either. Care to roll the dice by assigning one to auto?

Type inference works well in other languages, but it is more dangerous in C++ due to the risk of implicit conversions. C++ does so much stuff for you under the hood that it can be quite hard to figure out what is really going on, and auto only makes that problem worse. Use with caution.


> You have to admit, it's hilarious that the most natural swap implementation you could possibly write (and the one used in C++03) is now "standout terrible." It's not wrong to point that out, but it is wrong to assign blame to the hapless programmer instead of the language.

The point wasn't that your swap implementation was bad, but that you would never write swap yourself. It was made even worse for generic code because types are expected to be able to provide their own swap implementation.

> So why is vector<bool> the "real problem?" It's because its operator[] doesn't return a bool&, but instead some type "convertible to bool," that may do lots of other stuff too. But the standard is chock-full of language like that. For example, with iterators: `str.begin() != str.end()` Is that a bool? `str.begin()[2]`. Is that a char? The standard doesn't require either. Care to roll the dice by assigning one to auto?

But the standard says that sequence containers with operator [] must return a reference, not a type convertible to T. If you wrote your swap implementation against the requirements of a container, it is fine, the problem is vector<bool> is not a container. As for other parts of the standard that do permit the type to only be [contextually] convertible to some type T, there is no rolling of the dice, your code is correct or it is not.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: