Hacker News new | past | comments | ask | show | jobs | submit login
C++11 FAQ (stroustrup.com)
123 points by Tomte on Jan 26, 2017 | hide | past | favorite | 118 comments



I've been programming for money for over 20 years and I grew up professionally writing C and then half-migrating to C++, because the latter very naturally captured lots of C patterns and allowed for more succinct expression of the same. It was lean, expressive and elegant. Just needed a bit of Lisp-y closures and it would've been perfect.

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


This is perhaps one of the more unexpected opinions on C++ I've encountered. When I picked up C++ with Turbo C++ 3.0 in the early '90s, it was already widely regarded as an over-complex behemoth. Donald Knuth said of it in 1993, "Whenever the C++ language designers had two competing ideas as to how they should solve some problem, they said 'OK, we'll do them both'. So the language is too baroque for my taste." The last twenty years, there's no C++ that I would call lean, expressive, and elegant. You give the example of template meta-programming as something added more recently, but Erwin Unruh was printing the primes in compiler error messages not long after Knuth was knocking C++ as baroque. It makes me wonder if perhaps the issue isn't simply that over time you've become more aware of how complex the language has always been?

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.


Arguably the std::move stuff and the semantics around it have added uncomfortable complexity to the language, even if it might have been necessary. Other than that I think other advances in C++11 and beyond have made for a quite nicer language to work in.

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.


The 'original sin' of C++ is not having both references and pointers, but trying to remain compatible with C.

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.


Yeah I agree that C compatibility is the fail. But it's also the only reason C++ is around today. There were plenty of other compiled systems languages with OO or module systems floated in the 80s, including the quite capable Modula-2/3s, various Pascal dialects, etc. Of all of them only C++ (and Objective-C because iOS) has any relevance today. And likely because there was a kind of path to trendy-OO for existing C programmers and shops.


Surely references have intrinsic value to the programmer? They're a pointer that can't be moved or rebased. Other languages have only references, and in C++ you can use that convention and not have any extra confusion.


Except that references still can be bad: invalidated later on by an object deletion, no matter how many rules were enforced to assign the reference to begin with. And in an almost-worse sense, "sometimes" references can prevent this and sometimes not. Just not good enough, while syntactically making it harder to tell that references are being used in function calls and such.


References are crazy. There's no simple guideline to use them safely. Check this out:

    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.


Works for me in both C++03 and C++14? https://ideone.com/KhfGaN


Your code is not the same as my code and you're probably new to C++. I suggest trying two changes:

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 :-)


Sorry, can you clarify? Adding the template argument causes a crash on idone, but I have no idea why that happens. Moreover, the output on my computer is always b\n.

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?

[0] http://stackoverflow.com/a/2784304


So first of all, max("a", "b") expands to max<const char*>("a", "b"). It computes the maximum of two pointers, without looking at the characters at all. Comparing two pointers to unrelated objects is unspecified behavior, but in this case it usually returns the second pointer.

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.


Thank you for the explanation! I'll have to look out for that one...


> Arguably the std::move stuff and the semantics around it have added uncomfortable complexity to the language, even if it might have been necessary.

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.


No, the original sin was C's and it was the use of a separate "arrow" operator for accessing fields of a struct through a pointer. Other languages (C#, Java) successfully avoided that. C++ references allow templated code to be agnostic of whether an object is given by value or by reference, because the syntax is now more uniform.


> Arguably the std::move stuff and the semantics around it have added uncomfortable complexity to the language

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.


My perhaps naive uneducated opinion: they should have added syntactic sugar for it instead of making it look like a function. A new operator perhaps.


How does adding an operator help? There are two main advantage of having move semantics in the language and at least one of them relies on the fact that moves are implicit; you can write code that will move objects if supported and copy/destroy if not (for example, when returning an object or using an intermediate value in an expression). Adding an operator erases that advantage.

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.


> A new operator perhaps.

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__.


What if a hypothetical new operator used only characters already not allowed in identifiers and composed of characters not allowed in contiguous operator sequences.

Consider: :=, <-=, <|, $,


AFAIK, _ isn't an operator in Java (http://docs.oracle.com/javase/specs/jls/se8/html/jls-3.html#...). The language does allow _ to be used as a separator in numeric literals.

Is there a new operator in some upcoming version of the language specification?


_ is a keyword in Java 9.


It isn't an operator though, right?


That's correct, I only intended to clarify your point.


The thing is that something like computing primes with template instantiations was always a little weird and came with problems around sensible build processes. The rules were...well...complicated, but not that complicated. You had ADL rules that were weird, but basically, you could view a lot of the template hackery as a weird thing that you could understand without too much trouble, but probably didn't need to. That's much less true with most of the C++-11/14/17 features.

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.


> C++ has always been complex, but I defy anyone to read "Effective Modern C++" from cover to cover without clutching their pearls in horror.

Actually, Effective Modern C++ was pretty awesome. A good guide through all the complexity.


Oh, the book is awesome. I mean the fact that such a book had to be written is a pretty rough statement about the language.


> I find C++ today to be far simpler, more expressive

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 started with C++ around '92, but you are right, C++ was already complex back then, but it was still manageable, you could, sort of, hold its larger picture entirely in your head and still had some elegance to it.


1992 was just the year MFC came out. I would go as far as saying that C++ of the early '90s was at its Nadir of inelegance. I'd go as far as saying that C++ frameworks of the early 90s were the absolute nadir of elegance.

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.


> I would go as far as saying that C++ of the early '90s was at its Nadir of inelegance.

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.


Java picked momentum because in 1996, ANSI C++98 wasn't a thing, each compiler was catching up with the standard and many library vendors just gave use C libraries with us having to write C++ wrappers ourselves.

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.


> Java picked momentum because in 1996, ANSI C++98 wasn't a thing, each compiler was catching up with the standard and many library vendors just gave use C libraries with us having to write C++ wrappers ourselves.

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.


> 1992 was just the year MFC came out.

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.


OWL and TurboVision... now these are names I haven't heard in a long time. I remember the company I interned at was looking for TurboVision programmers at some point, none were found, soin a true Russian fashion they just wrote their own clone of it.


I used Turbo Vision in Turbo Pascal 6/7, then migrated to Turbo Pascal for Windows 1.5 with OWL and eventually to Turbo C++ 3.1 with OWL.


As a dissenting opinion from someone who has not quite 20 years of experience with C/C++ but 14, modern C++ is what brought me back to the language. The idioms allow me to use the language more functionally. More importantly, the STL is actually usable now and you can just code using it and std::algorithm without thinking of the nuts and bolts of move assignment and stuff at all. In the case where you need performance, you have the paradigm at your disposal. Also, you have a ref counted pointer thread safe pointer! A means of transferring ownership so heap allocated objects can also follow the same RAII pattern!

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++ still cannot compete with proper high level languages in this area.

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.


just be careful with those ref counted pointers: shared_ptr/weak_ptr introduce a lot of atomic variable access. if you use them from a single thread then that's pure overhead.


Yes, of course. A big chunk of my time at various previous companies was removing usage of such things when not necessary. It's also not "a lot" of atomic variable access but you do need a strong fence to provide the correct semantics (so it is some). To what degree "a lot" or "some" is depends obviously on what you're doing.


Honestly, back in '92 the language seemed easy, but the practice was hideously complex and error prone. Most people still couldn't even figure out how to do exceptions properly. I would argue that today, the language is more complex, but the practice is comparatively simpler (still complex as a ll get out, but compared to '92...).


C++ occupies the same mental space that languages like Haskell, Scala or OCaml.

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 agree. The other day I had to dive into the libigl [1] matrix structure (for those that don't know, a matrix is basically a 4x4 grid of numbers). What I found was over 10 levels of inheritance, along with rampant template use and macros _everywhere_. It actually took so long to parse I ended up just changing my algorithm completely to avoid having to use it. I have utmost respect for anyone that can work with C++ day to day, but I'll be staying away thanks.

[1] https://github.com/libigl/libigl/


The problem is that you can find bad code in any language. It's difficult to tell how much that is the fault of the language, and how much is the fault of that particular author.

I'm not defending C++ here, I actually agree with you, but it's difficult to provide decent evidence for its failings.


Doesn't libigl use Eigen for matrices? Eigen is a full-featured matrix library with a pretty decent API. If you just need a 4x4 grid of numbers, then you don't really need a library, sure. But once you need to do matrix arithmetic, basic linear algebra, various forms of indexing, mixing matrices of different types (e.g. float/double/int/etc), things get complicated, fast. A complex library like igl needs all this and more. I'm guessing this is why there's so many matrix libraries - everyone prefers rolling their own, after all, how hard can it be? (I'm guilty of this, so I'd know. But I reach for Eigen most of the time these days).


Odd. I wonder why they didn't just use Boost uBLAS or similar.


Last time I checked, Boost uBLAS was horribly slow and one should not use it. Use Eigen or Blitz++ (or just call BLAS directly) instead.


That's what I meant... Though Boost uBLAS is still way faster than a deeply inherited 4x4 matrix...


Interesting that you mention Rust... I am one of those "modern C++" kinds and I am quite curious and excited about Rust (haven't quite jumped boat yet though). I feel that there are quite a few others in such position.

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?


> move semantics

Move semantics have been great, in my experience (enabling things like unique_ptr). What is causing issues here?


Move semantics are very useful, but there's also something wrong if articles like this are necessary or even possible:

https://isocpp.org/blog/2012/11/universal-references-in-c11-...


Scott Meyer's articles (and numerous video presentations) on Universal References are educational but he doesn't make it clear to the audiences the bigger picture of where and why they'd need to really learn it.

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.


"char ∗(∗(∗a[])())()" may have entertainment value in an obfuscated C contest, but it is otherwise useless.

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.


>You're essentially saying, "don't worry, no one uses the new language features anyway".

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.


>Every computer science problem that involves one set of syntax to write another syntax (meta template programming) is complicated.

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...


>When I studied it, the "&&" syntax and type deduction rules seem to be "irreducible complexity".

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.


" A lot of C++ programmers can do a ton of productive work and get by for years without writing any templates from scratch" as well as people who choose not to have a stove or oven in their kitchen can eat, but with the delusion that they are smartly avoiding difficulties.


Oh gosh... how come does anyone not realize what a dead end a language has become once this kind of article needs to be written ?


My personal main problem is things like you can't template on an r-value reference, as T&& in a template matches both r-value and l-value references and then you need std::forward or std::move and then I start getting confused.

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".


If you want to template on an r-value reference, the easiest way is probably to use a forwarding reference and disable the template if the template parameter deduces to a reference

    template <class T, class = enable_if_t<!is_reference<T>::value>>
    auto f(T&& x);


Is this C++14? Can you please explain the "class = ..." template parameter?


Yes, that's C++14.

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).


class = X is just an anonymous template type parameter with default value X, just like

    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");
        ...
    }


I'm having trouble understanding the differences between a * and a &, and now with move semantics we have stuff like &&, && &, and && &&. Please stop!


I've come back to learn C++14 and it's _really_ nice. I love how lambdas, move semantics, templates, type inference, &c work. It leads to much cleaner and elegant code.

I'm still not so sure I like how iterators are designed, but maybe it'll grow on me?


But C++ was never easy to understand. For example, can you tell me off the top of your head, what are the conditions to get the compiler to generate a copy constructor and an assignment operator for a class? I have trouble answering that question even with the help of Google, and you're talking pre-Google times.


The rules really aren't that hard for practical use. Of course pedantic rules in the standard are much more complex and corner cases can bite us.

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.


That pocket knife is a collector's item. It includes most if not all the individual tools available in different models from Wenger. Victorinox also has one.

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 mistake is looking at C++ as just an enhanced C (understandable given the name, the marketing, etc.). It's kind of like when they marketed Windows as a DOS upgrade...

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.


What you paint as a fault might be an asset: C++ is multi-paradigm. There is more than one way to look at something and there is a joyous chaos of varied abstractions from which to choose from.

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.


> C++ is multi-paradigm

Yep, here it is - http://i.imgur.com/1oGHYqi.jpg :)


Just out of curiosity, which era of C++ did you like? C++98, or pre-Standard C++?

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.


How well has Perl fared? How about Lisp and Scala that are multi-paradigm, how well are they doing?


Perl was doing just fine until they fell in the perl6 hole and stagnated. Scala's getting its data science niche, and lisp has a lot more problems than just being multi-paradigm.


> Perl was doing just fine until they fell in the perl6 hole and stagnate

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.


The perl6 problem wasn't 'backward compatibility', the perl6 problem was 'this thing doesn't exist at all but it's sucking up all the momentum.'


C+11 is a different language. Learn it ;) And move semantics is a good thing. If you are fed up with C++ though - try Rust.


Template metaprogramming is not something new, it's old, and indeed much of Modern C++ (e.g. constexpr) is made to replace the complicated TMP with simpler and just-as-powerful abstractions.

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.


Modula-3. Bonus points are awarded to Modula-3 and Rust for safety.


Ada.


If Rust is slower than the equivalent C++, that's a bug. Please file them.


some of the pain points you refer to, esp move semantics, bring a lot of additional performance to the table. Would you feel the same way about C++ if you started now and only had to learn a clean C++11 subset going forward ?


I've often wondered how anybody manages to teach C++ today. I love working with the language, and I understand the reason behind many of the features, but I can't help but feel like it would be impossible to teach to beginners.


"...but I can't help but feel like it would be impossible to teach to beginners."

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.


The norm in the game industry is usually to expect some kind of background in low-level programming (maybe C, maybe some version of C++), and then teach new employees a very specific, restricted subset of C++ that's the in-house dialect (and every company has its own, different subset).


Fwiw, I followed this course at my university[1]. The 'schedule' and 'setup' links provide an idea of what is treated, and the full book is available[2]. The full course took slightly over six months, so yes, it takes a while :)

1. http://www.icce.rug.nl/edu/ 2. http://www.icce.rug.nl/documents/cplusplus/


that's because it is impossible. it takes years to master the language and that was before i lost touch with it around 2010 back when C++11 was TR1 or whatnot.

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.


I didn't use C-style string functions for the longest time. If I had a char*, perhaps because I got it from some C library we were using, I would turn it into a std::string and use it's functions and operators. One day, I realized how silly that was: I'm sure I was causing gratuitous memory allocations (not all strings qualify for the short string optimization).

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.


it isn't possible if you're doing real work, but it is possible if you're learning or teaching, especially if stdin and stdout are the only ways your program will do IO. it's actually pretty nice and code is surprisingly concise, especially if you use stream iterators.


Here is the course I was taught by at university:

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.


Maybe they don't bother. PSU is still teaching C++98.


That was the thing: starting with C++98, I was able to pick up C++11. But starting from scratch? I'm not sure.


Wow I feel the opposite. Because C++98 and earlier were so deficient in a lot of ways, people who got clever with it often ended up down a rabbit hole of template metaprogramming, boost:: bloat, custom classes for callback stuff, custom classes for reference counting or other smart pointers, etc. etc. Unlearning all that took me some time.

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.


Is the subset of boost in Google still the same as the public one?

https://google.github.io/styleguide/cppguide.html

It it is, I consider Google very lucky to have these who decided not to allow everything.


We have a rather lagerish codebase of C++11/14 and we manage to get people coming from other languages (Java / ObjC) to start working with it just fine. Modern C++ is pretty decent and readable (excluding some quirks of STL naming).

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 :)


One of C++'s most glaring problems is that everybody only ever uses some subset of some version of the language, but you still need to know basically the entire massive, monstrous language because not everybody uses the same subset and you need to be prepared for anything. On top of that, you're lucky if your employer or potential recipient of contributions (e.g., FOSS project) or whatever has actually codified the subset they want to you to use: most of the time, you need to infer the allowable subset from the existing code.


I would say Go is the first language since C where there is not really a subset to use. Every construct has a distinct purpose.


I would say that's cute.


It applies to most mainstream languages, even Python has subsets.


I've written code in over 50 different programming languages at this point (though only about a dozen or so professionally, including Python), and the only languages where I've ever been asked or required to work with a subset in lieu of the full language have been C++ and Perl. I've never worked on a project or for a company that allowed unrestricted use of all of C++.


The things you are complaining about are what make the language worth using-


I returned to C++ 3 years ago, having previously used it in the mid-1990s. I tend to agree with you, but I found that it was necessary to forget my old C mindset (pretty easy since it was 20 years old) and start learning modern C++ idioms from scratch.

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.


> fling raw pointers around with abandon.

Jitters in seat


I feel your pain but the canonical response is that you don't have to use the new features if you don't want to. If you have to work with other people's C++ code you may be in trouble.

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.


> ass-backwards closures

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].


Your link got eaten, but we don't have capture modifiers anymore. Captures are inferred, and you can override this with a keyword to take all captures by value instead. This still lets you get the same effect as C++, but we find that it's generally less verbose. It depends though!


> you can override this with a keyword to take all captures by value instead

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.


move semantics helps you to get rid of temporary objects. otherwise Object a,b,c; ... a = b + c; will create a temporary for b+c before it is assigned to a. The elegance of operator overloading got us there - no need for it without operator overloading.

Me too thought that template meta-programming is just a waste of time; i was surprised that std::tuple uses this stuff.


What's wrong with C++'s closures?


The less I use C++ the happier I am.


  #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.


Good stuff, I can also heartily recommend Scott Meyer's Effective Modern C++ book which covers idiosyncrasies and some unexpected behaviour (at least to me).

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 need to figure out what to do with my expensive old hard cover addition of the (C++98) C++ Programming Language. It just sits there looking sad. Not much value in it anymore.


It's a monument to progress; for maximum artistic and educational value, place it where you see it every day on top of decorative obsolete hardware (I recommend the Thinking Machines CM-2 for prettiness or SGI workstation for historical value).


I started to read into "Effective Modern C++" but it seemed the book (or at least the beginning) focused too much on obscure edge-cases of C++ for me before I stopped reading. "Effective C++" didn't feel that way for me and many tips were useful on a day-to-day basis. Is this just me or did you have a similar experience? I read those books quite some time ago so my brain could also wrong me here...


I noticed there were many edge cases in it but it happened to cover a lot of issues inside some C++11 code I'd written and I find out it quite enlightening, particularly the section regarding reference collapsing, type deduction and the bit on threading really not working like you would expect.

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.


Interesting to see the love/hate. We are just starting a new blank slate code base and decided to go straight to C++17. We need expressivity early on, high performance later. And a model where someone can really get high performance on the core and still let people prototype / experiment down the road. It's pretty well supported by gcc & clang which are the compilers we care about.

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.





Applications are open for YC Winter 2022

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: