In C++11 you can even have implicit conversion for multiple args constructors. But it is harder to use unintentionally than single argument implicit conversions.
This is why it's a rule in C++ that implicit conversions generate ambiguities and error messages like that. In such ambiguous situations, one interpretation has to be significantly "better" for it to be accepted without an error message. This is better than silently picking one of the possibilities when there's an ambiguity.
I would have thought that the no-conversion case (std::string vs std::string) would clearly beat the conversion case (Annie vs Annie), but I'm not really current on C++ conversion priority.
This sort of thing is why operator overloading isn't that great an idea. It seems attractive at first, but it gets complicated fast. As a rule of thumb, unless you're doing something mathematical, like matrices, for which the rules are well understood, don't overload mathematical operators.
This, combined with Python's lack of parameter type checking, can lead to strange results if a library function is called with the wrong type of numerical list object.
Compile-time safety doesn't protect you from being stupid when you choose the semantics of overloaded operators, just like compile-time safety doesn't protect you from giving your functions stupid names. My hypothesis is that so many C++ programmers abused operator overloading (even in the standard library, e.g. the absurdity of <<) that it made language designers remove it altogether from e.g. Java and many other languages.
The argument against operator overloading can be reduced to "programmers can't be trusted to design their classes properly".
And Java was exactly like that in the beginning, a language designed for the average developer, giving them only the basics they could be trusted with. Interestingly, Java's been busy adding back most of the things it decided against. Wouldn't be surprised if they add operator overloading in a future version.
<< is not IMO absurd, it makes streaming much nicer. The boost serialization & operator is however the kind of overloading that gives one pause when they see it.
Basically, for human-readable output << is far inferior to the alternatives that came before it and after it, and from an API perspective has nasty warts both in syntax and semantics.
* C's printf() is superior because it can be localized.
* Later formatting functions like Python's .format(), C# String.Format(), and C++ fmt::format() (from cppformat library) are far superior in normal use cases: they can be localized but they don't suffer from printf() safety problems.
* << suffers from severe problems in terms of syntax because it sits between arithmetic and comparison operators in precedence. So you can std::cout << x * 5; but you cannot std::cout << x & 0xff;
* iostream also suffers from the curse of statefulness. Unlike other formatting functions, changing the precision you want to use changes the properties of the IO stream itself, which is nonsensical. Either the properties should be bundled with the number or they should be lexically scoped.
Maybe << is nice for one or two things, but for the bread and butter of what developers do, it's absolutely terrible. I don't know how it makes "streaming nicer" because I'm not sure what you mean by streaming—the word has a few different possible contextual meanings, and I can't imagine how << makes any of those any easier.
The worst part of this is that they could have just used function call syntax and two of the major usability problems would have instantly disappeared. It would avoid problems with precedence and it would allow you to pass additional precision parameters.
Unfortunately, localization is really a hard (as in non-negotiable) requirement, and since << is so terrible at it, you are often forced to rip its usage out of a program rather than just work around it.
Sure, << has these problems and for more advanced use cases one can use Boost or the library you mentioned.
For localization one has to use a non-STL solution anyway, because the supporty is rudimentary in the standard lib.
On the other hand, there's nothing easier than using << for logging and concatenating + optionally converting strings and other types.
All in all the only real problem caused by << being an operator instead of a function is the precedence one; the rest are API design issues I would argue. Luckily it's a compiler warning (unused value) and if one adds another output op to the chain it will most likely be a compile error or still a warnig. So I still don't see why we have to condemn operator overloading because of <<.
This is an example of a non-local bug. Code contributed by different programmers at different places in the source can interfere. Those are troublesome on large projects.
I hear you about abusing overloading, but given how succinct it can make the syntax, I would not want to give it up. Its mix of a pragmatic and philosophical question. Just because some people may abuse it, do you make those suffer who can use it properly. Things get muddy when the set of people who can use it properly is really small. As far as C++ operator overloading is concerned I don't think that is the case. Some enlightened newbies go overboard but most handle it fine enough.
Something about D I like quite a bit is that they lean towards making these confusing things errors rather than have special case rules. C++ is full of complicated special case rules. Just consider template specialization and function overloading. The tie breaking rule is just insane. D community usually prefers making them errors rather blessing one of those ties to be kosher, till the community manages to come up with a compelling easy to remember rule.
I put the default constructor in private and mark the constructor I want everyone to use (typically the only one) as explicit. Seems safer than implicit conversions.
19 comments
[ 3.5 ms ] story [ 47.6 ms ] threadI should create a new subset of C++ and call it C += 0.5.
https://github.com/ErisBlastar/cplusequality
I would have thought that the no-conversion case (std::string vs std::string) would clearly beat the conversion case (Annie vs Annie), but I'm not really current on C++ conversion priority.
This sort of thing is why operator overloading isn't that great an idea. It seems attractive at first, but it gets complicated fast. As a rule of thumb, unless you're doing something mathematical, like matrices, for which the rules are well understood, don't overload mathematical operators.
A classic in Python:
But This, combined with Python's lack of parameter type checking, can lead to strange results if a library function is called with the wrong type of numerical list object.In this case I'm pretty sure this was a compiler bug, "no conversions" should have priority over "conversions"
And Java was exactly like that in the beginning, a language designed for the average developer, giving them only the basics they could be trusted with. Interestingly, Java's been busy adding back most of the things it decided against. Wouldn't be surprised if they add operator overloading in a future version.
<< is not IMO absurd, it makes streaming much nicer. The boost serialization & operator is however the kind of overloading that gives one pause when they see it.
* C's printf() is superior because it can be localized.
* Later formatting functions like Python's .format(), C# String.Format(), and C++ fmt::format() (from cppformat library) are far superior in normal use cases: they can be localized but they don't suffer from printf() safety problems.
* << suffers from severe problems in terms of syntax because it sits between arithmetic and comparison operators in precedence. So you can std::cout << x * 5; but you cannot std::cout << x & 0xff;
* iostream also suffers from the curse of statefulness. Unlike other formatting functions, changing the precision you want to use changes the properties of the IO stream itself, which is nonsensical. Either the properties should be bundled with the number or they should be lexically scoped.
Maybe << is nice for one or two things, but for the bread and butter of what developers do, it's absolutely terrible. I don't know how it makes "streaming nicer" because I'm not sure what you mean by streaming—the word has a few different possible contextual meanings, and I can't imagine how << makes any of those any easier.
The worst part of this is that they could have just used function call syntax and two of the major usability problems would have instantly disappeared. It would avoid problems with precedence and it would allow you to pass additional precision parameters.
Unfortunately, localization is really a hard (as in non-negotiable) requirement, and since << is so terrible at it, you are often forced to rip its usage out of a program rather than just work around it.
On the other hand, there's nothing easier than using << for logging and concatenating + optionally converting strings and other types.
All in all the only real problem caused by << being an operator instead of a function is the precedence one; the rest are API design issues I would argue. Luckily it's a compiler warning (unused value) and if one adds another output op to the chain it will most likely be a compile error or still a warnig. So I still don't see why we have to condemn operator overloading because of <<.
Something about D I like quite a bit is that they lean towards making these confusing things errors rather than have special case rules. C++ is full of complicated special case rules. Just consider template specialization and function overloading. The tie breaking rule is just insane. D community usually prefers making them errors rather blessing one of those ties to be kosher, till the community manages to come up with a compelling easy to remember rule.