The point of learning the "Modern" style is so that the newer, safer features are the ones you think of when writing new code. No matter which style you're first introduced to, you're going to end up coming into contact with a lot of different programming styles. Given that you may have to learn them all eventually, and that you've got to start somewhere, doesn't it make sense to introduce students to the features of the language that try to correct past mistakes?
A lot of what is considered modern C++ uses very advanced ideas. See this function from GOTW 94 [1], which Herb Sutter references approvingly, calling it "easy:"
Sutter likes this because it's written against an (conceptual) interface "Container", not an implementation like std::vector. He brags that not a single specific type appears anywhere in the code, making it much more powerful.
This is modern C++, and it is totally inaccessible to a newcomer. It hits you with templates, iterators, and several generic STL functions. Call it slightly wrong, and you'll get an avalanche of impenetrable error messages. Dig a little ("what does emplace_back do?") and you're confronted with allocator traits, placement new, perfect forwarding, etc.
Modern C++ is just not accessible to newcomers. The same thing in C (non-generically) would use concrete types and a for loop.
I've never written code like that, nor am I a C++ guru by any stretch (maybe I've written two hundred total hours of C++ in the last five years, and none before that), but that code makes complete sense to me at a brief scan. And, sure, it's not the simplest way to do it, but those simplest ways exist (and are superior to the way you'd do it in C)--I mean, the same thing in reasonable, idiomatic C++ would use a for loop and an iterator, and Sutter wouldn't yell at you for it. He might hope that eventually you become comfortable enough with templates to not be freaked out by that piece of code, but he wouldn't be mad at it.
It could be that I have experience with other statically typed languages, or that I have spent time internalizing why C++ works (from a conceptual, hands-off perspective) as opposed to just how, but...that seems reasonable to me. I probably wouldn't write it that way because I don't have code that relies on that kind of flexibility, but it's quite comprehensible. If your complaint is merely that C++ expects you to know stuff to read it...I mean...yeah, sure, it does, but if that's a problem we have deeper rot as professionals to deal with.
And it is a problem, given how much we venerate C while it keeps on being wholly unsafe at any speed. But, hey. Some of us need buffer overflows to stay employed, I guess.
Mentioning the job security factor of C in a C++ discussion is ... quite bold.
On average, C++ has the highest (talk + best practices + monstrous refactoring) / (actual code) ratio of any language I know.
The C++ projects I worked on moved at a glacial pace, with constant discussions about the best object hierarchy, patches because some compiler had a different interpretation of the 100+ nested templates than some other compiler, and so on.
I write and have written exclusively C++11 and C++14. I have not written legacy C++, nor do I have a cause to do so (though I have had to wrap around that code at times). The C++11 and C++14 code that I see is generally clean, adheres to straightforward memory models (heavy use of references and smart pointers), and has pretty straightforward and comprehensible OOP design.
There are developers who assert that C++11 is a step change of a language from C++98, and that may be true, but reasonably written, stdlib-adhering C++11 and C++14 are solid, hard-to-break languages in the general case and horror stories with regards to C++98 seem curious unless one wants to bring up failures of early DHTML applications in conversations about React.
(The build environments? Often a different story.)
> I write and have written exclusively C++11 and C++14.
I am skeptic of what this sentence is supposed to mean. Well, except that you've programmed in C++ for a very short period of time, in what seems like a very particular environment.
As far as I'm concerned. C++11 and C++14 came out with no design guidelines, zero different way to organize the code, no coding guidelines, no good practises. Just like C++03 before it :D
> I am skeptic of what this sentence is supposed to mean. Well, except that you've programmed in C++ for a very short period of time, in what seems like a very particular environment.
By choice. I use C++ where I absolutely must and nowhere else. It exists for particular use cases and no others. If I need high-performance native code with a strong(-enough) type system and I can't use Rust, I will use C++. Why would I use it elsewhere?
I have been using C++ for about five years, which by some lights is a short period of time--but, then, I've also been programming for twenty and I can comfortably say that I understand the hows and whys of C++ as a language even if I don't practice it heavily. (I learned them even before I used C++ at all because they reveal the particulars of the underlying techniques used by other programming environments, such as the JVM.)
And C++11 and later is pretty easily made straightforward, comprehensible, and manageable. (I mean, if you only stuck to the axioms of "never pass a pointer" and "always use std::unique_ptr and std::shared_ptr" you'll have code that's leaps and bounds past C++98/03 or anything written in C.) I write it in such a style and the code I'm exposed to generally does, too. Sorry that you don't?
You can read code in most languages and guess at what it does. But that doesn't mean you can write it - or debug it.
I'm not complaining. Herb Sutter has his arguments for why experts should write code this way.
But I don't think newcomers should attempt to write in this way. You can't understand iterators before you understand pointers, or templates before concrete types and functions, or emplace_back before push_back, or Concepts before classes, or auto before concrete types. You may be able to use them, but you won't understand them, and you'll be useless when things go wrong.
One last illustration: you mention your experience with statically typed languages. But C++ templates provide much less static type checking than non-template code. It is definitely not a place to start.
C++ templates provide type checking exactly within their bounds, which this particular example illustrates, so...well, what? Posit for a second that you are correct. I think you're not, but let's posit that you are. And then I just think...so what? I don't mean that to be flip: what's the point of your post if it's not to air complaints that "newcomers might have trouble"? I mean, yeah, all things being equal, approachability is nice--but newcomers have trouble with chainsaws, flying planes, and 240V wiring. Hard things are hard. C++ is hard. It's why it's a tool of last resort, if you are wise. If you're not, nothing's gonna help you, yeah?
While "easy" is rather subjective, I would say this: if you already know the basics of the standard library, then this is indeed quite easy. For the most part, it's not even remotely modern.
- it's a simple templated function; could even be reduced to one parameter if you use "typename Container::value_type" for Value
- it's using find (standard library algorithm)
- it's using begin and end (iterators common to all standard library containers)
- it's using emplace_back (new to C++11 with rvalue refs, but everyone already knows push_back which it replaces)
While emplace_back might be new to people using C++98, everything else is well over two decades old. It's extensively documented, and if you've ever done anything with the standard library, it's basic stuff. If any C++ programmer struggles to understand this, then they are essentially stating that they don't know the basics of the standard library or the language.
I wouldn't consider that anything close to beginner-level C++ because, as you said, it bombards you with a ton of concepts all at the same time. It's the kind of thing that I'd want put into some library and not look at it.
All that I'm trying to advocate are things like smart pointers instead of raw pointers and relying on iterators so you can use range-based for loops. Avoid some of the classic problems like forgetting to de-allocate things, de-allocating twice, using out-of-range indexes, and all that.