Not really, I was well aware of if-with-init and I still found this quite confusing. That one actually shows what the variable is initialized to. This one doesn't.
Only people who saw nothing bad in passing pairs .begin(), .end() in tons of places in c++ for like 30 years can say that thing like ‘auto&&’ improves anything.
`auto&&` has been standard syntax in C++ for going on 15 years now and has a very clear, irreplaceable meaning - a type-deduced forward reference - to any remotely competent developer.
package main
import "fmt"
var vec = []string{"the", "quick", "brown", "fox", "jumped", "over", "the", "lazy", "dog"}
func main() {
for i, s := range vec {
fmt.Printf("%d: %s\n", i, s)
}
}
Using `fmt.Println(i, ":", s)` inserts spaces on both sides of the colon, so the output is not quite right.
I find Go's `range` syntax easier to read than D's `(i, s; vec)` -- although generator functions that work with `range` have unaesthetic return types.
D has several usages for `foreach` depending on the number and type of its arguments. In this case, it sees that `vec` is an array, and so constructs a loop over the array. If `vec` was a range, the loop will be constructed as a loop over the array.
It's nice because it makes the syntax for arrays and ranges interchangeable, making for easy refactoring.
C++ needs to abandon iostreams. Didn't the C++ community acknowledge that it was a bad idea? In the early days of D, people did want to do a version of it for D, but I objected and currently nobody wants it.
> It seems to me that the C++ Standards Committee is doing a decent job maintaining the language, and introducing useful features when it makes sense to do so.
This can’t be further from truth. C++ is essentially Frankenstein’s monster.
I believe one of the main goals of whatever shadowy organizations are behind the "standards committee" is to sabotage the language to help clear the way for Rust.
If these people really have a great idea for what the future C++ should be like, then they should introduce that language all at once. Let us see their grand vision in one big package that can be embraced or rejected. This steady drip-drip-drip of half-baked features every few years is creating one hellish language.
I spend a lot of time in the Chromium source code and have seen plenty of boneheaded code, especially in places like Autofill where they seem to turn the newbies loose, but also in surprising places like V8, due to newbs using newfangled C++ "features" to do what worked perfectly well before in older idioms and was easier to read also.
Take for instance the function TryReduceFromMSB in v8/src/compiler/turboshaft/wasm-shuffle-reducer.cc -- do you see any reason that std::optional<uint8_t> max; couldn't just be a regular uint8_t? Let's name it 'max_used' also so the name is more descriptive and doesn't clobber the namespace.
The C++20 version is still clearly inferior to the Python and Lua examples because you still have to manually increment the counter in the loop body. IMO the sibling comment by HarHarVeryFunny has a much better C++ equivalent for this idiom, even if it's slightly more verbose.
> you still have to manually increment the counter in the loop body
It doesn't look like that to me, the ++i thing seems to be just to start printing the array from 1 (I don't know how things are in Python nowadays but I know in Lua arrays start at 1, so there's no need for something like this in there), the value of i is still increasing without telling it explicitly to do so
No, the increment is necessary to keep the counter going. The choice of pre-increment versus post-increment handles the "start at 1" issue you mentioned, but it isn't auto incrementing.
This is not an improvement. In fact it makes the code harder to understand. The way it's written makes people less familiar with this syntax think `int i = 0` and `auto &&it : vec` are two separate statements.
1> (mapdo (op put-line `@1: @2`) '#"how now brown cow" 0)
how: 0
now: 1
brown: 2
cow: 3
nil
mapdo: a mapping function for side effects of calling the function, not calculating a result, like map does.
op: produce a lambda expression out of an expression in which @1, @2, ... explicitly indicate the insertion of positional arguments, which are implicitly collected and become the parameter list of the lambda.
`...`: quasistring syntax: supports @ notations for interpolating. The @1, @2 elements of op do not require a double @@ inside a quasistring.
put-line: ordinary function to put a string to a stream (standard output by default) followed by newline. The lambda generated by op contains a (put-line ...) expression as its body, with @1 and @2 transformed into references to to generated, unique parameter names.
#"...": string list literal: contents are broken on whitespace and denote a list of strings #"foo bar" -> ("foo" "bar"). Requires ' quote in front to be quoted literally, and not evaluated as a compound expression applying the argument "bar" to the operator "foo". Yes, there is a #`...` quasi string list for templating over this.
nil: the value returned by mapdo after the side effects, printed by the REPL, not part of the output.
0: ordinary integer zero. But endowed with the power of being iterable. Where an iterable thing is required, 0 denotes the whole numbers 0, 1, 2, ... Similarly, 42 denotes 42, 43, ...
These are some of the ingredients produced by my one-member research programme into nicer Lisp coding.
2> (map (ret `@1: @2`) '#"how now brown cow" 0)
("how: 0" "now: 1" "brown: 2" "cow: 3")
map: take tuples by iterating over argument iterables in parallel, pass them to a function to project each tuple to a value, then return a list of values.
ret: cousin of op built on the same framework as op. Used for turning an expression into a lambda, when the expression isn't a compound form with an obvious operator. To turn (foo bar) into a lamdbda with op we use (op foo bar). But what if we have a simple variable x and want (lambda () x)? (op x) is not right, it means (lambda () (x)). (ret x) provides the sugar. Here, it lets us spin up a two-argument function that evaluates a quasistring.
The way this website shows the programming languages is odd. They're blue and slanted and if you hover your mouse cursor over them they have a color transition, so you'd think they are links - and yet you click on them and nothing happens
Give me the explicit C syntax any day over this monstrosity. I refuse to write anything other than C++98, the last version of C++ that built on C without trying to turn it into a completely different language.
I guess I am officially old and no longer know what the cool "it" is any more. The C++17 example seems much more clear to me. It is more typing, but so what? It is not that much more typing, and it looks like code that people have written for 40 years.
I am so happy I haven't written a line of C++ in like 15 years. Absolutely disgusting language. Every time I look up one of their new standards, I'm like how does anyone keep all of this in their head (usually on top of stuff like boost, etc.)? No wonder LLMs are a thing.
C++ is a decades-long mistake that spawned other misguided directions in the entire software industry. We're still trying to undo many of the bad ideas, but I'm afraid even Rust has too much of C++ in it, not only in terms of syntax but mentality and culture. LLMs are making things worse by automatically generating code in such bloated languages, where people have less need to even look at the horrific spaghetti inside. The whole thing needs to be reconsidered from the ground up, from first principles, by actual creative thinking human beings with lessons of hindsight.
> Gall's Law: A complex system that works is invariably found to have evolved from a simple system that worked. A complex system designed from scratch never works and cannot be patched up to make it work. You have to start over with a working simple system.
This is at least a bit more pythonic:
`auto&&` has been standard syntax in C++ for going on 15 years now and has a very clear, irreplaceable meaning - a type-deduced forward reference - to any remotely competent developer.
I find Go's `range` syntax easier to read than D's `(i, s; vec)` -- although generator functions that work with `range` have unaesthetic return types.
It's nice because it makes the syntax for arrays and ranges interchangeable, making for easy refactoring.
for (auto [i,v] : std::views::enumerate(vec)) std::cout << i << ": " << v << std::endl;
FWIW C++23 also has a python-like print and println:
std::println("{}: {}", i, v);
C++ needs to abandon iostreams. Didn't the C++ community acknowledge that it was a bad idea? In the early days of D, people did want to do a version of it for D, but I objected and currently nobody wants it.
This can’t be further from truth. C++ is essentially Frankenstein’s monster.
If these people really have a great idea for what the future C++ should be like, then they should introduce that language all at once. Let us see their grand vision in one big package that can be embraced or rejected. This steady drip-drip-drip of half-baked features every few years is creating one hellish language.
I spend a lot of time in the Chromium source code and have seen plenty of boneheaded code, especially in places like Autofill where they seem to turn the newbies loose, but also in surprising places like V8, due to newbs using newfangled C++ "features" to do what worked perfectly well before in older idioms and was easier to read also.
Take for instance the function TryReduceFromMSB in v8/src/compiler/turboshaft/wasm-shuffle-reducer.cc -- do you see any reason that std::optional<uint8_t> max; couldn't just be a regular uint8_t? Let's name it 'max_used' also so the name is more descriptive and doesn't clobber the namespace.
If anything it is more of a Chimera
It doesn't look like that to me, the ++i thing seems to be just to start printing the array from 1 (I don't know how things are in Python nowadays but I know in Lua arrays start at 1, so there's no need for something like this in there), the value of i is still increasing without telling it explicitly to do so
Their C++17 example prints starting from 0. Probably a mistake.
If you look at the linked page for C++20[0], other types can be put in the initializer statement, so it's unlikely the loop auto-increments.
[0]: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p06...
op: produce a lambda expression out of an expression in which @1, @2, ... explicitly indicate the insertion of positional arguments, which are implicitly collected and become the parameter list of the lambda.
`...`: quasistring syntax: supports @ notations for interpolating. The @1, @2 elements of op do not require a double @@ inside a quasistring.
put-line: ordinary function to put a string to a stream (standard output by default) followed by newline. The lambda generated by op contains a (put-line ...) expression as its body, with @1 and @2 transformed into references to to generated, unique parameter names.
#"...": string list literal: contents are broken on whitespace and denote a list of strings #"foo bar" -> ("foo" "bar"). Requires ' quote in front to be quoted literally, and not evaluated as a compound expression applying the argument "bar" to the operator "foo". Yes, there is a #`...` quasi string list for templating over this.
nil: the value returned by mapdo after the side effects, printed by the REPL, not part of the output.
0: ordinary integer zero. But endowed with the power of being iterable. Where an iterable thing is required, 0 denotes the whole numbers 0, 1, 2, ... Similarly, 42 denotes 42, 43, ...
These are some of the ingredients produced by my one-member research programme into nicer Lisp coding.
map: take tuples by iterating over argument iterables in parallel, pass them to a function to project each tuple to a value, then return a list of values.ret: cousin of op built on the same framework as op. Used for turning an expression into a lambda, when the expression isn't a compound form with an obvious operator. To turn (foo bar) into a lamdbda with op we use (op foo bar). But what if we have a simple variable x and want (lambda () x)? (op x) is not right, it means (lambda () (x)). (ret x) provides the sugar. Here, it lets us spin up a two-argument function that evaluates a quasistring.
> Gall's Law: A complex system that works is invariably found to have evolved from a simple system that worked. A complex system designed from scratch never works and cannot be patched up to make it work. You have to start over with a working simple system.
Linus Torwalds famously said that subset is zero. You're not allowed to use C++ in Linux, a wise move.
Here's Google's: https://google.github.io/styleguide/cppguide.html Search for "do not use" and you'll find plenty of hits.
here you go: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines
Reminds me of Good Parts, Bad Parts meme of JS.