C++11 FAQ
stroustrup.com
stroustrup.com
And then things got completely out of hand. Elegance was nuked from the high orbit in favor of "power features" and abstraction. Fucking Boost (I can't tone it down, sorry), move semantics, ass-backwards closures, meta-template programming - C++ that was easy to understand became something that would normally come out of self-indulgent academic research.
Again, I've been using C/C++ for all my professional life, so it pains me to say this, but what it has become, it's... it's just horrible. Anything - C, D, Python, Rust - is cleaner and more elegant than C++ and just as powerful. If nothing else, I feel sorry for Bjarne. What could've been a gem of programming languages became http://i.imgur.com/1oGHYqi.jpg
Of course good code reviews and testing is a must to help people to get up to speed and stop shooting themselves in the foot :)
In my 5 years at Google, I've watched many developers start from scratch on our subset of modern C++. There doesn't seem to be a huge challenge with it.
https://google.github.io/styleguide/cppguide.html
It it is, I consider Google very lucky to have these who decided not to allow everything.
i understand that what is usually considered good form is trying to completely forget about C features like pointers and arrays and use their STL counterparts like unique/shared_ptr, std::vector, std::array and iterators. this gets you a nice modern subset of the language that makes it relatively difficult to shoot yourself in the foot.
Perhaps part of my hangup is the expectation that you would teach the difference between, say, enum and enum class. There are good reasons to use enum, and I believe there always will be. There are good reasons to use pointers (in fact, one of the main reasons I use pointers is to point to the payload of a std::unique_ptr, and I use std::unique_ptr when I have an obvious owner, even if I might need other functions to access the payload while the owner is still around).
So I don't think that it's possible to ignore "the old ways" completely.
1. http://www.icce.rug.nl/edu/ 2. http://www.icce.rug.nl/documents/cplusplus/
http://www.dcs.bbk.ac.uk/~roger/cpp/
Lots of value came from the lectures with roger. Sadly he has retired, it would have been nice to have them up on youtube.
So a relatively easy way to learn C++ is as an 'upgrade' to C, starting of with picking the parts of the language that help you the most:
* you start by already knowing C, but maybe not feeling super productive, using it as structs + functions
* you set up the C++ compiler (most modern version available), and just continue writing C
* you start using streams to print (because it can be more convenient than printf)
* you start using std::vector and std::string, arrays being one of the big pain points of C
* you start using `auto` and for-in loops
* you start using simple classes, without any inheritance. just structs with methods
* you start adding operators - so much fun!
* you start learning about references and const, and suddenly all your method arguments become `const T& v`
* you learn about not using pointers (as much on stack as possible), move semantics, shared/unique pointers. This means you'll also use more templates.
* you deepen your understanding of writing your move/copy constructors. Learn RIAA, and become a fan of `}`.
* you learn about constexpr and constexpr functions
* this is already a pretty happy subset of C++, and as you learn to use templates, you may at some point start writing one yourself. Same with lambdas - if you need them you learn them.
Move semantics have been great, in my experience (enabling things like unique_ptr). What is causing issues here?
https://isocpp.org/blog/2012/11/universal-references-in-c11-...
The Forwarding Reference (aka Universal Reference) is for programmers writing generic templates. A lot of C++ programmers can do a ton of productive work and get by for years without writing any templates from scratch. Yes, they use others' templates (such as STL/Boost libraries) but they don't necessarily write their own. I have no survey data but I'll take a wild guess that less than 1% of C++ programmers need to memorize template Forward References.
As an analogy using the plain C Language, it's as if someone created very prominent blogs about the complexity of "char ∗(∗(∗a[])())()" with long paragraphs explaining how it's "an array of pointers to functions returning pointer to function returning pointer to char".
If that type of essay had a lot of visibility, people might think that "Wow the C Language is difficult and too complicated!" In reality, a lot of C programmers don't write complex declarations like that. The more common pointer-to-function for something like qsort() is very easy to write.
Using && in templates may be advanced C++, but it is both useful and mainstream.
You're essentially saying, "don't worry, no one uses the new language features anyway". But that's not a convincing response to a complaint about the incredible complexity introduced by a particular new language feature.
No, I'm not saying that. A lot of C++ programmers are application programmers who glue together Qt GUI with some code to read a TCPIP socket or file and write out some data. Many C++ programmers simply don't have the day-to-day need to write generic templates (libraries).
Yes, it may be useful and mainstream to you but I don't think that is reflective of the whole C++ programming community at large.
>But that's not a convincing response to a complaint about the incredible complexity introduced by a particular new language feature.
When I studied it, the "&&" syntax and type deduction rules seem to be "irreducible complexity". Every computer science problem that involves one set of syntax to write another syntax (meta template programming) is complicated. This includes XSLT to rewrite XML, or PHP code that dynamically writes javascript syntax embedded inside of HTML syntax. Reading any of that code-that-writes-code is always ugly. If we were to challenge a language designer to recreate a syntax that meets the same exact goals of C++ (perfectly pass parameters without casts, no unnecessary copies that kills performance, with static type checks, etc), he'd eventually end up at the same complexity you have now. In other words, there is no breakthrough A.I. compiler algorithm that lets the parser correctly infer how parameters pass through to generic code. If there's a whitepaper that claims otherwise, I'd love to read it. (Rust generics are simpler but they also do less than C++ templates.) The TLDR is that C++ templates is a code gen tool and like any other nontrivial code gen tool, they end up being complicated.
That may well be, given a particular set of constraints. But that is exactly the issue with C++ today. It's a conglomerate of powerful and diverse features, and so everything that gets added interacts in countless ways with everything that's already there. There's no orthogonality at all. Everything is both irreducibly and impossibly complex at the same time.
You're right that meta programming is inherently more complex than other issues. But it's not the only area where C++'s complexity is outgrowing our (or at least my) cognitive capacity if the goal is to write reasonably reliable code in a reasonably productive way.
I'm not sure about 'meta' part you mentioning, but it seems that all the complexity of c++ templates comes from declarativity of these. If they were imperative — e.g. 'hey, compiler, put an additional if into specialization for that particular type', or produce specializations in a loop, or create template generators, etc — that would feel less bizarre. Deep inspection of seemingly simple terralang.org turned that upside down to me. These two languages (luajit/terra) are both not very popular, but the idea of simple imperative codegen is right there. I wish I were able to replace terra part with just C...
I wish in templates they had gone with the suggestion I saw to use T&&& to mean "r-value or l-value reference", and then let T&& just mean "r-value reference".
template <class T, class = enable_if_t<!is_reference<T>::value>>
auto f(T&& x); auto foo(int = 0);
declares a function with a int parameter with default value 0. In this case, it's just a conventional place to stick an expression in the declaration to trigger SFINAE. If you don't need SFINAE, you can also just place the condition in a static_assert in the definition template <class T>
auto foo(T&&) {
static_assert(!is_reference<T>::value, "call me with an r-value");
...
}First, "class = enable_if_t<!is_reference<T>::value>" is just "class Dummy = enable_if_t<!is_reference<T>::value>" that doesn't bother to name Dummy. It's providing a default template argument for a function template (this is a C++11 feature; in C++03, only class templates could have this).
Next, "enable_if_t<!is_reference<T>::value>" is a C++14 library feature (enable_if_t is an alias template; the Core Language gained alias templates in C++11, the library gained these in C++14). It is a synonym for "typename enable_if<!is_reference<T>::value>::type", which is more verbose (and what one had to type in C++11).
"is_reference<T>::value" is a C++11 type trait, that is true when T is X& or X&&, and false otherwise. (It's implemented with partial specializations, and a "static const bool value = true;" data member - actually constexpr these days, but no difference there. This is type traits 101.)
enable_if is a library helper for SFINAE. (Not necessarily C++11 Expression SFINAE; here, just C++03 classic SFINAE is being used.) Basically, enable_if<true>::type is void. enable_if<false>::type doesn't exist. This works with the Core Language's SFINAE rules to make function templates vanish from the overload set (if you don't know what SFINAE is, look it up; it's a little complicated, but it exists for good reasons, and it goes back to C++98).
So, this is how one writes a function template that vanishes under certain criteria. This is often desirable compared to doing different things for different types (that can often be done with "tag dispatch") or emitting compiler errors for certain types (that's a static_assert).
Yes it takes a certain openness of mind and a long learning curve. But then, you get to operate at a level that other platforms can only dream.
In full disclosure, I do NOT consider myself a good C++ programmer. But I've always respected the fact that it's one environment which fosters using the whole toolbox instead of just a hammer... no matter how elegant that hammer may be. In that respect, it reminds me a lot of Perl.
i.e. backward compatibility is king.
Paraphrasing Stroustrup, there are two kinds of languages, those that are full of old cruft and those that are too green to have been proven in the field.
Yep, here it is - http://i.imgur.com/1oGHYqi.jpg :)
At least since C++98, C++ has always had the "multi-paradigm problem". If you try to use every language feature and paradigm C++ has to offer, you are going to suffer. Writing and maintaining a large C++ codebase has always required restricting yourself to a subset of the language.
And precisely the reason we like Rust is because it is like the new C++ (moves, lean lambdas, templates, on-the-stack values, borrowing) without so much of the old stuff. What do you think? What do you expect from Rust?
The problem is that it's not useful as such and I guess most people wouldn't buy one. I wouldn't mind receiving one as a gift though :)
Regarding C++, if I remember my programming language history right, there aren't many languages that are that old, popular, available in so many domains and still evolving to keep up with the times.
Language design has progressed a lot, the hardware has advanced at an exceptional pace. Today's context is very different.
I say we wait another 20 years to see how Rust & D are doing. Python might be old, but it only really became popular this century, as Perl declined. I estimate that C will continue its slow decline with time.
The language has continued to grow, but it's growing in a rather different direction (or rather in a clear direction): towards a modern, preferred subset that _is_ lean, expressive, and elegant. While C++ is still far from my favorite language, I find C++ today to be far simpler, more expressive, and more elegant than at any other point in the past.
I love C++11 and use it every day, but I think putting references in the language was an 'original sin'. They should have stuck with C's model: it's either a value or a pointer to a value. References just added a pile of complexity and pontificating about whether something was being copied, moved, or passed by reference.
const string& s = max<string>("a", "b");
cout << s;
Standard types and functions, const references, no warnings, everything by the book. But it crashes.There's a similar can of worms related to slicing (treating Derived* as Base* is silently unsafe because arrays and copy constructors exist). C++ is scary.
1) Replace max("a", "b") with max("b", "a"). Run it, look closely at the result, and be enlightened.
2) Replace max("a", "b") with max<string>("a", "b"). Run it, look at the result, and be enlightened in a different way.
As a small hint, your code invokes unspecified behavior, while my code invokes undefined behavior.
If this exercise doesn't put the fear of C++ into you, then I don't know what will :-)
EDIT: Not sure if this is it, but after some digging I've found [0] on lifetimes of temporaries. As for the lacking template argument, presumably const char* is inferred and a temporary string is constructed in main?
As for max<string>("a", "b"), it implicitly converts both arguments into temporary strings, then returns a const reference to one of them. Since it's a reference to a temporary, it becomes dangling when max returns, but you can assign it to a const string& anyway. When you try to access it, you'll get garbage or crash.
Compared to using std::auto?
Nah man. std::move is far easier to understand than trying to shove both concepts down operator=. std::unique is simply easier to use than std::auto by any real measurement.
That'd invalidate a lot of programs. Just like how the new _ operator in Java is killing a lot of code right now.
Every symbol that can be used... has been used. "Move" is the meaning of the operator in any case. Its no less readable than say... Python's __lshift__ or __add__.
Consider: :=, <-=, <|, $,
Is there a new operator in some upcoming version of the language specification?
The other advantage is that you can force only move semantics which allows enforcing things like ownership. Adding an operator doesn't destroy this advantage, but it is a bit silly since you're just adding line noise for something that really should be implicit anyway.
Unless you just literally don't like the spelling of "std::move" which seems pretty lame since it should be relatively rare anyway.
That means that Stroustrup couldn't have ditched pointers, but if he wanted to implement operator overloading for value types with same efficiency as C he needed some way to support doing that without using pointers directly: http://www.stroustrup.com/bs_faq2.html#pointers-and-referenc... http://stackoverflow.com/questions/8007832/could-operator-ov...
A lot of the nastiness of C++ comes from trying to maintain C compatibility: Special behavior for POD, most vexing parse, .h/.cc file split, everything preprocessor and many other misfeatures. Of course, there's also the flip side of it: if C++ wasn't a near-superset of C, it would have never become so popular.
I think the main pain with move semantics was that it had to be bolted on to the language later. Consider that factor, they did an amazing job with it.
I have a much easier time holding the entire picture together but others are correct in saying you have to throw away old conceptions and ideas of the language. I personally have thrown away the way I think about C entirely (at least when I code in modern C++).
C++ is precisely useful because it's a better C: it has a C-like subset, and works directly with C data structures and function call interfaces. Plus it has features to do that kind of programming in a safer, more managed way.
I would absolutely not use C++ for anything that isn't in the area of "this should be ideally written in C, but that would bring in undue difficulties, risks and time".
The more C++ moves away from its focus of being a language for writing low-levelish systems middleware, the more it loses its relevance.
> Also, you have a ref counted pointer thread safe pointer!
Had those in 1997 using draft ISO C++, thanks.
STL only started getting into compilers around '95, so we had no standard containers and dozens of different string implementations. The only part of the standard library widely available was iostreams, which is easily the most despised part of that library.
With no useful standard library to speak of, C++ devs were basically using the C standard library, rife with such shining gems of perfection as scanf(), strcat() and dear_hacker_please_inject_shellcode_here(). Since no generic containers were in sight, pretty much all 90s code I've had the pleasure to see was infested with macros. Pieces of exquisite beauty. I still have nightmares sometimes.
Then came the late 90s and early 2000s. The STL was widely supported, Boost started to gain ground and Andrei Alexandrescu released Modern C++ Design. Template metaprogramming slowly got out of hand and both code and errors were indecipherable.
The thing is, C++11 and beyond is the very opposite of this. Hairy template metaprogramming is replaced by simple and straightforward constexprs. Proper lambdas obsoleted functors, a gang of bloated design patterns (Visitor, Obeserver, Strategy, Command, Template Method, Decorator and so on) and several complicated lambda libraries from Boost.
Modern C++ is still a mess, but I believe it's way more elegant than ever before.
Yup. '92 C++ was what gave Java such a boost. If we'd somehow started with '98 C++, or better still C++11, Java might never have picked up so much momentum.
Also in 2000 I was writing K&R C on a HP-UX compiler, let alone the state of C++ compilers for writing portable code.
Yes. The experience in the early 90's meant everyone was looking for a saner choice. By the time C++ was the saner choice, Java had already received a giant boost.
Ignoring the nice work of PowerPlant , Turbo Vision and Object Windows Library, because they were too high level for C devs.
So Microsoft ripped off their Application Frameworks (Afx) prototype and we got MFC instead.
C++ has always been complex, but I defy anyone to read "Effective Modern C++" from cover to cover without clutching their pearls in horror.
Don't get me wrong, I actually like C++ in a weird way. There's nothing else out there that goes quite so far in putting you the programmer in charge while still attempting to let you work at a slightly higher level. But the price in cognitive load for this flexibility is just stupidly high.
Actually, Effective Modern C++ was pretty awesome. A good guide through all the complexity.
That is logically impossible, since it hasn't removed anything.
The myopic mantra "just don't use what you don't like" breaks down as soon as you work in a team, where you don't dictate what other's cannot use (let alone what code they inherit).
I'm not defending C++ here, I actually agree with you, but it's difficult to provide decent evidence for its failings.
If you define no constructors then the compiler will generate a default constructor with no args, a copy constructor, a move constructor and an assignment operator. If any class data members don't have one of those then the class you are writing won't have one either.
The simplest practical rule is define none of them or define all of them. It is not often, perhaps one in ten classes, that you should do something else.
If wind up using C++ for more than a small project, than you ought read up and not use simple heuristics like this.
Can you name another language that supports the many high-level abstractions that C++ supports (OOP, generic programming) while being just as fast? Only D and Rust come to mind, and they are neither as fast nor, crucially, as well-developed as C++. The amount of resources and existing code in C++ is staggering. Newer languages such as D and Rust simply can't compete.
Eg, back in the days, I could fling raw pointers around with abandon. Now they need to be handled with care, and there are many tools and patterns available for you not to have to deal with them at all.
Jitters in seat
I have the same arc as you, but every time I come back to C++ (from C# or Python) I'm surprised how much I enjoy it. I look at the new stuff more as a way to bring features from scripting or functional languages to a real language - makes me happy.
that's very surprising. I would say that C++ lambda expressions have been an incredible success and probably the best thing to come out of C++11.
For example, rust started with boxed closures, because that's what all the 'proper' languages do. They were forced to switch to distinctly-typed, unboxed closures with similar capture modifiers to be competitive with C++ [1].
that's what I meant by modifiers :). It is not unlike [&] and [=] I guess (except for the "tiny" detail that lifetimes are checked).
BTW, I'm not sure what the [1] reference in my comment was supposed to point to though.
Compared to its real competition, C++ is very elegant and a joy to use. These languages are meant for enjoying the maximum out of compile-time type abstractions, not as an easy-to-use tool for simple enterprise apps.
I'm still not so sure I like how iterators are designed, but maybe it'll grow on me?
#include "parent-comment"
Me too, exactly. C++ has turned to baroque junk. The last useful new feature C++ acquired was to make string literals const. (When was that, 2005 or 6?)If you're using C++, it's best to stick with C++03.
The rabid experimenters who run the ISO C++ committee should have started their own language 15 years ago, rather than hijacking an industry standard to everyone's detriment.
In reality, C++ is an entirely different language that provides partial C compatibility (in fairness, it is more compatible than just about any other language out there) in order to improve adoption and minimize the impedance mismatch with systems interfaces (which, at least back in the day, were predominantly C, and largely still are).
If we called "C++" "NotC", and then wrapped all the C compatibility features (type coercion, malloc()/free(), C strings, etc.) with a "unsafe mode" flag, it'd all make much more sense.
Me too thought that template meta-programming is just a waste of time; i was surprised that std::tuple uses this stuff.
Also Stroustrup's 4th edition of The C++ Programming Language is invaluable, if not just for the more-readable sans-serif font compared to the 3rd edition! And of course the coverage of all C++11 changes in it...
I can also recommend the Overload journal for interesting C++ articles (https://accu.org/index.php/journals/c78/)
And cppcon videos on YouTube - there's a wealth of info there, free.
Also the ISO CPP FAQ (which I think supersedes Stroustrup's?): https://isocpp.org/faq
I have not read the previous ones so not really sure on the style difference with them. I wonder if it depends on the sort of code you were writing - I was making use of std::thread and incorrectly assuming a certain behaviour.
I haven't really done much C++ programming in 20 years so approached it as a brand new language (to me) that I'd never used before. It's a big but powerful standard. I can't say I adore the syntax, but it's subject to path-dependent constraints that you just have to put up with.
I'm still looking for good references to style and practice that are appropriate to the latest C++. Books targeted at C++11 are pretty useful but it's too early to have more than the standard to help with 1y features.
Modern C++ Coding guidelines:
https://github.com/isocpp/CppCoreGuidelines/blob/master/CppC...