Clearly, it's over 9000!
... more seriously though, overloading the comma operator is a bad idea and you shouldn't do it. In fact, almost nobody does it.
Specifically, you don't want to break the principle of least astonishment with a command such as
v+= 1,2,3,4,5;
Assuming you want to add up a bunch of literals, what you could do is something like: std::array data { 1,2,3,4,5 };
v += std::reduce(data.begin(), data.end());
and if you've implemented some <algorithm> and <numeric> wrappers for containers, you would have something like an template<class Container>
typename Container::value_type
reduce(const Container& container);
and then you would write v += reduce(std::array{ 1,2,3,4,5 } );
which is terse and much clearer.Nope... std::vector::insert() takes an std::initializer_list [1].
[1] https://en.cppreference.com/w/cpp/container/vector/insert
The rest of the article isn't wrong, but it fails to establish why anyone thinks this pattern is "nice".
The only use that seems absolutely in my opinion is when the overload is absolutely transparent mathematically, for instance to implement geometrical transformations on a Matrix class.
That works because in this case you don't actually end up with "custom" behavior, you just expand the standard and well understood notation of the language by plugging the standard and well understood mathematical notation for matrix operations. Anybody who understands this mathematical notation will be able to understand what the code does without additional context.
Anything beyond that is just asking for trouble IMO. It's basically code obfuscation.
Of course C++ has precedent for that sort of insanity, especially with the IMO absolutely bonkers use of the bit shift operators for... input/output processing. A notation that you'll note hasn't had a lot of success outside of C++.
Quite a moderate position in practice. There are a substantial number of programmers who enjoy being able to tell what basic syntax does from memory without having to cross-reference documentation and source files.
And as you allude to, the example in question isn't a sum; it is an append. It would be far more appropriate to have an a = append(a, {1,2,3,4,5}) style construct. Or something with pointers if efficiency is important. Or construct the vector inline in older versions of C++. The += should be reserved for actual vector summing.
This whole article stands as a reminder that C++ is a remarkable language, and is starting to make up a lot of ground on achieving feature parity with Common Lisp.
It can be abused but most of the time is extremely convenient. Iostream and boost set a terrible example however. Boost especially goes into the deep end on templates, operators, crazy dependencies, unacceptable compile times etc. It really isn't fair to judge modern C++ on boost. Instead of being batteries included, boost is now more a last resort.
Overloading the comma operator doesn't cause any particular danger, it merely provides a mildly unfavourable situation in which an unnecessary mistake is possible: there's "threat" of the default comma operator that does something completely different, it's not very obvious whether type something or something& or something&& is considered, and the involved overload resolution rules are somewhat lawyer-grade.
a, b = 10, 20;
(This assigns 10 to b but leaves a untouched)I never liked the comma operator, even in C. It's that kind of thing that "looks nice" at first but it is confusing in the end. It's not syntactic sugar, it's syntactic raisins.
v += [1, 2, 3, 4, 5] makes sense to me
or, for a less ambiguous meaning
v.extend([1, 2, 3, 4, 5]);
is just fine.
I don't think it makes sense to view comma as an operator.
V+={1,2,3,4,5};I would pick a different syntax, too, but this is more flexible than std::iota. You can do v+=a,b,c, for example.
I also would think any decent compiler would completely optimize away that appender instance, making it quite efficient for short lists of items (for longer lists, compiling a push_back call for each item would get inefficient, memory and cache-wise. I don’t see a compiler converting that into a loop)
In addition, more templates and more types means way less compiler throughput. That is why major parts of Boosts are avoided.
Not to mention human throughput too.
> compiling a push_back call for each item would get inefficient, memory and cache-wise?
That depends a lot on what you are doing.
By the way, if you are dealing with tons of constants in your code, then it usually means the design of the application is likely inflexible and a code smell overall.
Extra careful to cover all cases is when I start looking at alternatives. In this case it is not even enhancing readability. Even if you may be an excellent programmer think whether your team can be that extra careful most of the times before introducing these quirks in your code.
IMO, an advantage of C++ is that, in well-written code, being extra careful is (mostly) limited to the implementer of code, not to its users.
It would be nice if nobody would have to be extra careful, but that’s the price you pay for power, I fear.
For example, instead of a comma operator and its overloads, you can have tuples. Then `let (a,b) = (b,a)` works as expected. If you want weird code, you can overload `vec += (1,2,3,4,5)` with a tuple, and that's a straightforward case with no hidden gotchas.
Subsequent to the birth of Boost Assign, the language improved:
for (int i : {1,2,3,4,5})
v.push_back(i);
Simple, built in, anyone can understand it.One thing I like about the C (not ++) language is that it's always clear what function that is being called by looking at the function name. In C++ you have to guess a lot of times (or look very carefully).
The problem is that the behavior of the code silently changes when a function is moved and this is because of the overloading feature in C++. You can get those types of problems in other cases too when you overload functions that have a good name, but maybe it's less likely since I guess the comma operator is already defined for all different types but named functions have to be defined by the programmer.
Every value computation and side effect of the first (left) argument of the built-in comma operator , is sequenced before every value computation and side effect of the second (right) argument. [1]
The same sequencing guarantee is not applied to a user-defined comma operator.
For the similar reasons it's not a great idea to overload && and || either.
[1] https://en.cppreference.com/w/cpp/language/eval_order (point 9)