195

With the new standard, there are new ways of doing things, and many are nicer than the old ways, but the old way is still fine. It's also clear that the new standard doesn't officially deprecate very much, for backward compatibility reasons. So the question that remains is:

What old ways of coding are definitely inferior to C++11 styles, and what can we now do instead?

In answering this, you may skip the obvious things like "use auto variables".

Toby Speight
  • 27,591
  • 48
  • 66
  • 103
Alan Baljeu
  • 2,383
  • 4
  • 25
  • 40

9 Answers9

176
  1. Final Class: C++11 provides the final specifier to prevent class derivation
  2. C++11 lambdas substantially reduce the need for named function object (functor) classes.
  3. Move Constructor: The magical ways in which std::auto_ptr works are no longer needed due to first-class support for rvalue references.
  4. Safe bool: This was mentioned earlier. Explicit operators of C++11 obviate this very common C++03 idiom.
  5. Shrink-to-fit: Many C++11 STL containers provide a shrink_to_fit() member function, which should eliminate the need swapping with a temporary.
  6. Temporary Base Class: Some old C++ libraries use this rather complex idiom. With move semantics it's no longer needed.
  7. Type Safe Enum Enumerations are very safe in C++11.
  8. Prohibiting heap allocation: The = delete syntax is a much more direct way of saying that a particular functionality is explicitly denied. This is applicable to preventing heap allocation (i.e., =delete for member operator new), preventing copies, assignment, etc.
  9. Templated typedef: Alias templates in C++11 reduce the need for simple templated typedefs. However, complex type generators still need meta functions.
  10. Some numerical compile-time computations, such as Fibonacci can be easily replaced using generalized constant expressions
  11. result_of: Uses of class template result_of should be replaced with decltype. I think result_of uses decltype when it is available.
  12. In-class member initializers save typing for default initialization of non-static members with default values.
  13. In new C++11 code NULL should be redefined as nullptr, but see STL's talk to learn why they decided against it.
  14. Expression template fanatics are delighted to have the trailing return type function syntax in C++11. No more 30-line long return types!

I think I'll stop there!

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Sumant
  • 4,286
  • 1
  • 23
  • 31
  • 7
    Great answer, but I would strike `result_of` from the list. Despite the cumbersome `typename` needed before it, I think `typename result_of()(std::declval()...)`, and with the acceptance of [N3436](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3436.html) into the working paper they both work for SFINAE (which used to be an advantage of `decltype` that `result_of` didn't offer) – Jonathan Wakely Jan 18 '13 at 09:17
  • Regarding 14) I'm still crying that I have to use macros in order to write the same code twice -- once for the function body and once for the decltype() statement... –  Mar 31 '14 at 22:56
  • 2
    I would like to note that this topic is linked from [this Microsoft page](https://msdn.microsoft.com/library/vstudio/hh279654(v=vs.120).aspx) as a "For more information" article in a general introduction to C++ language, but this topic is an highly specialized one! May I suggest that a brief **"This topic is NOT for C++ novices!"** advice be included at beginning of the topic or this answer? – Aacini Apr 11 '15 at 05:41
  • Re 12: "In-class member initialization" - that's the new idiom, not a deprecated idiom, isn't it? Switch the sentence order perhaps? Re 2: Functors are very useful when you want to pass around types rather than objects (especially in template parameters). So it's only _some_ uses of functors that are deprecated. – einpoklum Jun 17 '16 at 20:59
  • Re in-class member initialization: In C++17, you can initialize even the static members in the class definition. – einpoklum Jan 13 '19 at 20:19
  • Re: Prohibiting heap allocation. Unfortunately std::make_shared will always work because it doesn't new up a stack_only_t. Deleting new will still work for std::make_unique. – Nick H Oct 15 '19 at 10:05
66

At one point in time it was argued that one should return by const value instead of just by value:

const A foo();
^^^^^

This was mostly harmless in C++98/03, and may have even caught a few bugs that looked like:

foo() = a;

But returning by const is contraindicated in C++11 because it inhibits move semantics:

A a = foo();  // foo will copy into a instead of move into it

So just relax and code:

A foo();  // return by non-const value
Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
  • 9
    The preventable mistakes can however now be caught by using reference qualifiers for functions. Such as in the above case defining `A& operator=(A o)&` instead of `A& operator=(A o)`. These prevent the silly mistakes and make classes behave more like basic types and do not prevent move semantics. – Joe Jun 03 '13 at 15:21
61

As soon as you can abandon 0 and NULL in favor of nullptr, do so!

In non-generic code the use of 0 or NULL is not such a big deal. But as soon as you start passing around null pointer constants in generic code the situation quickly changes. When you pass 0 to a template<class T> func(T) T gets deduced as an int and not as a null pointer constant. And it can not be converted back to a null pointer constant after that. This cascades into a quagmire of problems that simply do not exist if the universe used only nullptr.

C++11 does not deprecate 0 and NULL as null pointer constants. But you should code as if it did.

Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
38

Safe bool idiomexplicit operator bool().

Private copy constructors (boost::noncopyable) → X(const X&) = delete

Simulating final class with private destructor and virtual inheritanceclass X final

Community
  • 1
  • 1
kennytm
  • 510,854
  • 105
  • 1,084
  • 1,005
24

One of the things that just make you avoid writing basic algorithms in C++11 is the availability of lambdas in combination with the algorithms provided by the standard library.

I'm using those now and it's incredible how often you just tell what you want to do by using count_if(), for_each() or other algorithms instead of having to write the damn loops again.

Once you're using a C++11 compiler with a complete C++11 standard library, you have no good excuse anymore to not use standard algorithms to build your's. Lambda just kill it.

Why?

In practice (after having used this way of writing algorithms myself) it feels far easier to read something that is built with straightforward words meaning what is done than with some loops that you have to uncrypt to know the meaning. That said, making lambda arguments automatically deduced would help a lot making the syntax more easily comparable to a raw loop.

Basically, reading algorithms made with standard algorithms are far easier as words hiding the implementation details of the loops.

I'm guessing only higher level algorithms have to be thought about now that we have lower level algorithms to build on.

Klaim
  • 67,274
  • 36
  • 133
  • 188
  • 8
    Actually there is a good excuse. You're using [Boost.Range's](http://www.boost.org/doc/libs/1_48_0/libs/range/doc/html/range/reference/algorithms/introduction.html) algorithms, which are much nicer ;) – Nicol Bolas Feb 15 '12 at 18:42
  • @NicolBolas Haha yes obviously, if standard library could use ranges it would be even better. – Klaim Feb 15 '12 at 18:44
  • 10
    I don't see that `for_each` with a lambda is any better than the equivalent range-based for loop, with the contents of the lambda in the loop. The code looks more or less the same, but the lambda introduces some extra punctuation. You can use equivalents of things like `boost::irange` to apply it to more loops than just those that obviously use iterators. Plus the range-based for loop has greater flexibility, in that you can exit early if required (by `return` or by `break`), whereas with `for_each` you'd need to throw. – Steve Jessop Feb 15 '12 at 18:49
  • @NicolBolas That said, there are far less algorithms available than in the current standard library. – Klaim Feb 15 '12 at 18:49
  • 3
    @SteveJessop It have been explained by far better people than me before, but basically a loop is a loop, while a for_each is "a loop that goes through all the elements of a 'range'". In practice (after having used this way of writing algorithms myself) it feels far easier to read something that is built with straightforward words meaning what is done than with some loops that you have to uncrypt to know the meaning. That said, making lambda arguments automatically deduced would help a lot making the syntax more easily comparable to a raw loop, so I understand your feeling. – Klaim Feb 15 '12 at 18:54
  • 5
    @SteveJessop: Even so, the availability of range-based `for` makes the usual `it = c.begin(), const end = c.end(); it != end; ++it` idiom defunct. – Ben Voigt Feb 15 '12 at 19:03
  • @Klaim: He's talking *specifically* about the `for_each` algorithm. It is equivalent to the range-based for syntax in C++11. And he points out how much it's better than that, since you can `continue`, `break`, `return`, and things you can't with lambdas. The range-based for is not "a loop"; it's specifically a loop over all the elements of a range. – Nicol Bolas Feb 15 '12 at 19:04
  • @Klaim: Of course there are fewer algorithms. The point of the Boost.Range algorithms is that you can *combine* them. By pairing begin and end iterators, you can chain range algorithms. So things like "count_if" are unnecessary in Boost.Range; you just use a filtering algorithm, and pipe that output into a counting algorithm. – Nicol Bolas Feb 15 '12 at 19:12
  • 7
    @SteveJessop One advantage of the `for_each` algorithm over the range based for loop is that you _can't_ `break` or `return`. That is, when you see `for_each` you know immediately without looking at the body that there's no such trickiness. – bames53 Feb 15 '12 at 19:18
  • 1
    @bames53: true, but if you're that worried about it, then (a) the loop body is too long for your preferred "at-a-glance" code-reading style, and (b) you're probably also worried it might throw. I don't think we can hope for much in C++ (or any other language with exceptions) along those lines, although people who write code analysis tools might be able to tell me that exceptions are in some important way more tractable than `break`. Luckily for me, I have syntax coloring, so I can pretty easily identify early loop exits. – Steve Jessop Feb 15 '12 at 19:23
  • 5
    @Klaim: to be specific, I'm comparing for example `std::for_each(v.begin(), v.end(), [](int &i) { ++i; });` with `for (auto &i : v) { ++i; }`. I accept that flexibility is double-edged (`goto` is very flexible, that's the problem). I don't think that the constraint of not being able to use `break` in the `for_each` version compensates for the extra verbosity it demands -- users of `for_each` here are IMO sacrificing actual readability and convenience for a kind of theoretical notion that the `for_each` is *in principle* clearer and conceptually simpler. In practice it isn't clearer or simpler. – Steve Jessop Feb 15 '12 at 19:36
  • 1
    @SteveJessop Obviously there are tradeoffs between the two. A very short and simple loop body means that the lambda syntax and iterators used in `for_each` is relatively heavy, but I don't think the tradeoffs will always come out in favor of range based for. Though to be fair I'm one of those people that used algorithms with bind1st, bind2nd, and standard functors before we had lambda and bind, so what I find more readable isn't necessarily the same as what others find readable (e.g. for a loop that increments every element in an array I think `transform` rather than `for_each`). – bames53 Feb 15 '12 at 20:32
  • @BenVoigt: Nitpick: `it = c.begin(), const end = c.end();` is not valid, `const` applies to all declarations. – Sebastian Mach Feb 16 '12 at 07:33
  • for_each has never had much love. Before C++11, its usefulness was reduced by having to write a non-local function. Lambdas saved the day, but along came range-for statements and hit it out of the park. I feel sorry for for_each. – Jon Feb 24 '12 at 13:03
  • @bames53, to solve the "no break or return" issue: for (auto &i : v) {func();}. Now you know that func() can't break or return out of your loop, with the added advantage that you didn't need to resort to for_each or lambdas. – Jon Feb 24 '12 at 23:51
  • @Jon This defeats the purpose of locality of code initiated by both lambda and for each loop. – Klaim Feb 25 '12 at 00:53
10

You'll need to implement custom versions of swap less often. In C++03, an efficient non-throwing swap is often necessary to avoid costly and throwing copies, and since std::swap uses two copies, swap often has to be customized. In C++, std::swap uses move, and so the focus shifts on implementing efficient and non-throwing move constructors and move assignment operators. Since for these the default is often just fine, this will be much less work than in C++03.

Generally it's hard to predict which idioms will be used since they are created through experience. We can expect an "Effective C++11" maybe next year, and a "C++11 Coding Standards" only in three years because the necessary experience isn't there yet.

Philipp
  • 48,066
  • 12
  • 84
  • 109
  • 1
    I'm doubtful of this. Recommended style is to use swap for move and copy construction, but not std::swap because that would be circular. – Alan Baljeu Feb 17 '12 at 16:47
  • Yeah but the move constructor usually calls a custom swap, or it is essentially equivalent. – Inverse Feb 19 '12 at 16:46
2

I do not know the name for it, but C++03 code often used the following construct as a replacement for missing move assignment:

std::map<Big, Bigger> createBigMap(); // returns by value

void example ()
{
  std::map<Big, Bigger> map;

  // ... some code using map

  createBigMap().swap(map);  // cheap swap
}

This avoided any copying due to copy elision combined with the swap above.

Andrzej
  • 5,027
  • 27
  • 36
  • 1
    In your example the swap is unnecessary, copy elision would construct the return value in `map` anyway. The technique you show is useful if `map` already exists, rather than just being constructed. The example would be better without the "cheap default constructor" comment and with "// ..." between that construction and the swap – Jonathan Wakely Jan 31 '14 at 09:57
  • I changed it as per your suggestion. Thanks. – Andrzej Jan 31 '14 at 14:03
  • The use of "big" and "Bigger" is confusing. Why not explain how the sizes of the key and the value type matter? – einpoklum Jun 17 '16 at 21:05
2

When I noticed that a compiler using the C++11 standard no longer faults the following code:

std::vector<std::vector<int>> a;

for supposedly containing operator>>, I began to dance. In the earlier versions one would have to do

std::vector<std::vector<int> > a;

To make matters worse, if you ever had to debug this, you know how horrendous are the error messages that come out of this.

I, however, do not know if this was "obvious" to you.

einpoklum
  • 118,144
  • 57
  • 340
  • 684
v010dya
  • 5,296
  • 7
  • 28
  • 48
  • 1
    This feature was added already in the previous C++. Or at least Visual C++ implemented it per standards discussion many years earlier. – Alan Baljeu Apr 23 '15 at 02:58
  • 1
    @AlanBaljeu Of course, there are many non-standard things being added to compiler/libraries. There were tons of compilers that had "auto" variable declaration before C++11, but then you couldn't be sure that your code can actually be compiled by anything else. The question was about the standard, not about "was there any compiler that could do this". – v010dya Apr 23 '15 at 05:57
1

Return by value is no longer a problem. With move semantics and/or return value optimization (compiler dependent) coding functions are more natural with no overhead or cost (most of the time).

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Martin A
  • 132
  • 10
  • ... but which idiom has been deprecated? – einpoklum Jun 17 '16 at 21:05
  • Not an idiom but it was a good practice not needed anymore. Even with compiler supported RVO which is optional. https://en.wikipedia.org/wiki/Return_value_optimization "In the early stages of the evolution of C++, the language's inability to efficiently return an object of class type from a function was considered a weakness....." struct Data { char bytes[16]; }; void f(Data *p) { // generate result directly in *p } int main() { Data d; f(&d); } – Martin A Jun 19 '16 at 14:13
  • I was hinting you should phrase your answer as "the custom of avoiding return by value is no longer relevant as etc. etc. etc." – einpoklum Jun 19 '16 at 17:09