Effective STL: 50 Specific Ways to Improve Your Use of the Standard Template Library

Category: Programming
Author: Scott Meyers
4.3
All Stack Overflow 64
This Year Stack Overflow 1
This Month Stack Overflow 9

Comments

by anonymous   2019-07-21

The answer is not trivial. If you have 2 main sections in your software: 1st setup, 2nd lookup and lookup is used more than setup: the sorted vector could be faster, because of 2 reasons:

  1. lower_bound <algorithm> function is faster than the usual tree implementation of <set>,
  2. std::vector memory is allocated less heap page, so there will be less page faults while you are looking for an element.

If the usage is mixed, or lookup is not more then setup, than <set> will be faster. More info: Scott Meyers: Effective STL, Item 23.

by anonymous   2019-07-21

Here is an example of using a functor to perform two accumulations in parallel.

struct MyFunctor
{
    // Initialise accumulators to zero
    MyFunctor() : acc_A(0), acc_B(0) {}

    // for_each calls operator() for each container element
    void operator() (const T &x)
    {
        acc_A += x.foo();
        acc_B += x.bar();
    }

    int acc_A;
    int acc_B;
};


// Invoke for_each, and capture the result
MyFunctor func = std::for_each(container.begin(), container.end(), MyFunctor());

[Note that you could also consider using std::accumulate(), with an appropriate overload for operator+.]

As for virtual functors, you cannot do these directly, as STL functions take functors by value, not by reference (so you'd get a slicing problem). You'd need to implement a sort of "proxy" functor that in turn contains a reference to your virtual functor.* Along the lines of:

struct AbstractFunctor
{
    virtual void operator() (const T &x) = 0;
};

struct MyFunctor : AbstractFunctor
{
    virtual void operator() (const T &x) { ... }
};

struct Proxy
{
    Proxy(AbstractFunctor &f) : f(f) {}
    void operator() (const T &x) { f(x); }
    AbstractFunctor &f;
};

MyFunctor func;
std::for_each(container.begin(), container.end(), Proxy(func));

* Scott Meyers gives a good example of this technique in Item 38 of his excellent Effective STL.

by Timo Geusch   2019-07-21

There is a companion book to the Effective C++ series, which is called "Effective STL". It's a good starting point for learning about best practises using the Standard C++ library (neé STL).

by anonymous   2019-07-21

Scott Meyers describes this technique very well in Effective C++.

by user283145   2019-07-21

The meaning of that quote should be taken at face value. For the majority of the STL algorithms, you should not implement your predicate functor such that it has observable state (AKA "side effects"), because:

  • the iteration order over the container is not defined,
  • the algorithm is free to make copies of the functor,
  • the algorithm may depend on statelessness in order to not corrupt the container contents or crash.

The simplest way to enforce this upon yourself is to define operator() as const.

There are exceptions, such as for_each, for which none of the above apply. You are free to use stateful functors here. For more info, see this excellent article: http://drdobbs.com/cpp/184403769.

Behind the scenes, the authors of your STL implementation are free to write the remove_if (and other algorithms) any way they like, so long as it conforms to the requirements laid down by the standard. There's no real reason to worry too much about exactly why you're getting the behaviour you're seeing, beyond acknowledging that it's undefined. If you want to know the specifics, I would just take a look at the code for remove_if in the STL implementation that you're using.

As for your side-note; this is not "corruption", it's simply an artifact of how remove_if works (this would occur even for a valid predicate). The only requirement is that all the elements to the left of pos are valid (because they are to be retained). There are no requirements on what elements exist from pos onward (see here). (Chapter 32 of "Effective STL" by Scott Meyers has a good explanation of why remove_if (and so on) behave like this).

by anonymous   2017-08-20

TL;DR

Extra parentheses change the meaning of a C++ program in the following contexts:

  • preventing argument-dependent name lookup
  • enabling the comma operator in list contexts
  • ambiguity resolution of vexing parses
  • deducing referenceness in decltype expressions
  • preventing preprocessor macro errors

Preventing argument-dependent name lookup

As is detailed in Annex A of the Standard, a post-fix expression of the form (expression) is a primary expression, but not an id-expression, and therefore not an unqualified-id. This means that argument-dependent name lookup is prevented in function calls of the form (fun)(arg) compared to the conventional form fun(arg).

3.4.2 Argument-dependent name lookup [basic.lookup.argdep]

1 When the postfix-expression in a function call (5.2.2) is an unqualified-id, other namespaces not considered during the usual unqualified lookup (3.4.1) may be searched, and in those namespaces, namespace-scope friend function or function template declarations (11.3) not otherwise visible may be found. These modifications to the search depend on the types of the arguments (and for template template arguments, the namespace of the template argument). [ Example:

namespace N {
    struct S { };
    void f(S);
}

void g() {
    N::S s;
    f(s);   // OK: calls N::f
    (f)(s); // error: N::f not considered; parentheses
            // prevent argument-dependent lookup
}

—end example ]

Enabling the comma operator in list contexts

The comma operator has a special meaning in most list-like contexts (function and template arguments, initializer lists etc.). Parentheses of the form a, (b, c), d in such contexts can enable the comma operator compared to the regular form a, b, c, d where the comma operator does not apply.

5.18 Comma operator [expr.comma]

2 In contexts where comma is given a special meaning, [ Example: in lists of arguments to functions (5.2.2) and lists of initializers (8.5) —end example ] the comma operator as described in Clause 5 can appear only in parentheses. [ Example:

f(a, (t=3, t+2), c);

has three arguments, the second of which has the value 5. —end example ]

Ambiguity resolution of vexing parses

Backward compatibility with C and its arcane function declaration syntax can lead to surprising parsing ambiguities, known as vexing parses. Essentially, anything that can be parsed as a declaration will be parsed as one, even though a competing parse would also apply.

6.8 Ambiguity resolution [stmt.ambig]

1 There is an ambiguity in the grammar involving expression-statements and declarations: An expression-statement with a function-style explicit type conversion (5.2.3) as its leftmost subexpression can be indistinguishable from a declaration where the first declarator starts with a (. In those cases the statement is a declaration.

8.2 Ambiguity resolution [dcl.ambig.res]

1 The ambiguity arising from the similarity between a function-style cast and a declaration mentioned in 6.8 can also occur in the context of a declaration. In that context, the choice is between a function declaration with a redundant set of parentheses around a parameter name and an object declaration with a function-style cast as the initializer. Just as for the ambiguities mentioned in 6.8, the resolution is to consider any construct that could possibly be a declaration a declaration. [ Note: A declaration can be explicitly disambiguated by a nonfunction-style cast, by an = to indicate initialization or by removing the redundant parentheses around the parameter name. —end note ] [ Example:

struct S {
    S(int);
};

void foo(double a) {
    S w(int(a));  // function declaration
    S x(int());   // function declaration
    S y((int)a);  // object declaration
    S z = int(a); // object declaration
}

—end example ]

A famous example of this is the Most Vexing Parse, a name popularized by Scott Meyers in Item 6 of his Effective STL book:

ifstream dataFile("ints.dat");
list<int> data(istream_iterator<int>(dataFile), // warning! this doesn't do
               istream_iterator<int>());        // what you think it does

This declares a function, data, whose return type is list<int>. The function data takes two parameters:

  • The first parameter is named dataFile. It's type is istream_iterator<int>. The parentheses around dataFile are superfluous and are ignored.
  • The second parameter has no name. Its type is pointer to function taking nothing and returning an istream_iterator<int>.

Placing extra parentheses around the first function argument (parentheses around the second argument are illegal) will resolve the ambiguity

list<int> data((istream_iterator<int>(dataFile)), // note new parens
                istream_iterator<int>());          // around first argument
                                                  // to list's constructor

C++11 has brace-initializer syntax that allows to side-step such parsing problems in many contexts.

Deducing referenceness in decltype expressions

In contrast to auto type deduction, decltype allows referenceness (lvalue and rvalue references) to be deduced. The rules distinguish between decltype(e) and decltype((e)) expressions:

7.1.6.2 Simple type specifiers [dcl.type.simple]

4 For an expression e, the type denoted by decltype(e) is defined as follows:

— if e is an unparenthesized id-expression or an unparenthesized class member access (5.2.5), decltype(e) is the type of the entity named by e. If there is no such entity, or if e names a set of overloaded functions, the program is ill-formed;

— otherwise, if e is an xvalue, decltype(e) is T&&, where T is the type of e;

— otherwise, if e is an lvalue, decltype(e) is T&, where T is the type of e;

— otherwise, decltype(e) is the type of e.

The operand of the decltype specifier is an unevaluated operand (Clause 5). [ Example:

const int&& foo();
int i;
struct A { double x; };
const A* a = new A();
decltype(foo()) x1 = 0;   // type is const int&&
decltype(i) x2;           // type is int
decltype(a->x) x3;        // type is double
decltype((a->x)) x4 = x3; // type is const double&

—end example ] [ Note: The rules for determining types involving decltype(auto) are specified in 7.1.6.4. —end note ]

The rules for decltype(auto) have a similar meaning for extra parentheses in the RHS of the initializing expression. Here's an example from the C++FAQ and this related Q&A

decltype(auto) look_up_a_string_1() { auto str = lookup1(); return str; }  //A
decltype(auto) look_up_a_string_2() { auto str = lookup1(); return(str); } //B

The first returns string, the second returns string &, which is a reference to the local variable str.

Preventing preprocessor macro related errors

There is a host of subtleties with preprocessor macros in their interaction with the C++ language proper, the most common of which are listed below

  • using parentheses around macro parameters inside the macro definition #define TIMES(A, B) (A) * (B); in order to avoid unwanted operator precedence (e.g. in TIMES(1 + 2, 2 + 1) which yields 9 but would yield 6 without the parentheses around (A) and (B)
  • using parentheses around macro arguments having commas inside: assert((std::is_same<int, int>::value)); which would otherwise not compile
  • using parentheses around a function to protect against macro expansion in included headers: (min)(a, b) (with the unwanted side effect of also disabling ADL)
by Klaim   2017-08-20

I think you'd better have some lectures about good practices and why they are good. That should help you more than a code analysis tool (in the beginning at least).

I suggest you read the series of Effective C++ and **Effective STL books, at least. See alsot The Definitive C++ Book Guide and List

by litb   2017-08-20

Beginner

Introductory, no previous programming experience

Introductory, with previous programming experience

* Not to be confused with C++ Primer Plus (Stephen Prata), with a significantly less favorable review.

Best practices


Intermediate


Advanced


Reference Style - All Levels

C++11/14 References:

  • The C++ Standard (INCITS/ISO/IEC 14882-2011) This, of course, is the final arbiter of all that is or isn't C++. Be aware, however, that it is intended purely as a reference for experienced users willing to devote considerable time and effort to its understanding. As usual, the first release was quite expensive ($300+ US), but it has now been released in electronic form for $60US.

  • The C++14 standard is available, but seemingly not in an economical form – directly from the ISO it costs 198 Swiss Francs (about $200 US). For most people, the final draft before standardization is more than adequate (and free). Many will prefer an even newer draft, documenting new features that are likely to be included in C++17.

  • Overview of the New C++ (C++11/14) (PDF only) (Scott Meyers) (updated for C++1y/C++14) These are the presentation materials (slides and some lecture notes) of a three-day training course offered by Scott Meyers, who's a highly respected author on C++. Even though the list of items is short, the quality is high.

  • The C++ Core Guidelines (C++11/14/17/…) (edited by Bjarne Stroustrup and Herb Sutter) is an evolving online document consisting of a set of guidelines for using modern C++ well. The guidelines are focused on relatively higher-level issues, such as interfaces, resource management, memory management and concurrency affecting application architecture and library design. The project was announced at CppCon'15 by Bjarne Stroustrup and others and welcomes contributions from the community. Most guidelines are supplemented with a rationale and examples as well as discussions of possible tool support. Many rules are designed specifically to be automatically checkable by static analysis tools.

  • The C++ Super-FAQ (Marshall Cline, Bjarne Stroustrup and others) is an effort by the Standard C++ Foundation to unify the C++ FAQs previously maintained individually by Marshall Cline and Bjarne Stroustrup and also incorporating new contributions. The items mostly address issues at an intermediate level and are often written with a humorous tone. Not all items might be fully up to date with the latest edition of the C++ standard yet.

  • cppreference.com (C++03/11/14/17/…) (initiated by Nate Kohl) is a wiki that summarizes the basic core-language features and has extensive documentation of the C++ standard library. The documentation is very precise but is easier to read than the official standard document and provides better navigation due to its wiki nature. The project documents all versions of the C++ standard and the site allows filtering the display for a specific version. The project was presented by Nate Kohl at CppCon'14.


Classics / Older

Note: Some information contained within these books may not be up-to-date or no longer considered best practice.

by anonymous   2017-08-20

The guide to the language by Stroustrup:

Scott Meyers's triple are quite useful: Effective C++, More Effective C++, and Effective STL.

Also see these questions:
- https://stackoverflow.com/questions/155762/best-c-resource
- Java FAQ equivalent of C++ FAQ lite?
- C++ : handle resources if constructors may throw exceptions (Reference to FAQ 17.4]
- What C++ pitfalls should I avoid?
- New to C++. Question about constant pointers

by anonymous   2017-08-20
char* found = std::find(arr, arr+9, ' ');

Note that 'no match' is signaled wuth the end iterator:

bool match = (arr+9) != found;

Note, that

  • binary search doesn't apply unless you characters are in some known ordering.
  • std::find is inlined, templatized and will perform to the max if you turn on optimization (e.g. -O3 -march=native for g++)

Edit since you have shown more code, I now realize you actually want to detect (sub)string length. You could use

  • std::string::find_first_of
  • std::string::find_last_of
  • std::string::find
  • std::string::rfind etc.

Of course, that assumes you'd want to convert the char[] to std::string for the purpose. In practice, that might be a perfectly valid idea, because of SSO (Small String Optimization) found in nearly all implementations of the C++ standard library. (see Items 13-16 in Herb Sutter's More Exceptional C++, or Scott Meyers' discussion of commercial std::string implementations in Effective STL).

by anonymous   2017-08-20

I was in the same situation 12 years ago when I got my first programming job out of college. I didn't do any open source but I managed to gain a lot of practical and advanced C++ knowledge by reading books (the dead tree kind).

In particular, there was an excellent series by Scott Meyers which I feel helped the most in turning me from newbie to professional:

Effective C++: 55 Specific Ways to Improve Your Programs and Designs

More Effective C++: 35 New Ways to Improve Your Programs and Designs

Effective STL: 50 Specific Ways to Improve Your Use of the Standard Template Library

The topics in these books range from beginner to advanced. It took me about 2 years working in C++ to understand every chapter in these books, so don't be disheartened if it goes over your head at some point... just try reading it again later :)

by anonymous   2017-08-20

The reason for the error is that you are calling the copy constructor of auto_ptr my_player in fooPlayerFactory::MakePlayerO() which is a const method. That means that is cannot modify its members.

However the copy constructor of auto_ptr DOES modify the right hand side so returning my_player trys to change its pointer to 0 (NULL), while assigning the original pointer to the auto_ptr in the return value.

The signature of the copy constuctor is

auto_ptr<T>::auto_ptr<T>(auto_ptr<T> & rhs)

not

auto_ptr<T>::auto_ptr<T>(const auto_ptr<T> & rhs)

The copy constructor of auto_ptr assigns ownership of the pointer to the left hand side, the right hand side then holds nothing.

I don't think you want to use auto_ptr here, you probably want boost::smart_ptr

It looks like you have mixed up two uses for auto_ptr

The first is as poor man's boost::scoped_ptr. This is to manage a single instance of a pointer in a class, the class manages the life time of the pointer. In this case you don't normally return this pointer outside your class (you can it is legal, but boost::smart_ptr / boost::weak_ptr would be better so clients can participate the life time of the pointer)

The second is its main purpose which is to return a newly created pointer to the caller of a function in an exception safe way.

eg

auto_ptr<T> foo() {
    return new T;
}

void bar() {
    auto_ptr<T> t = foo();
}

As I said I think you have mixed these two uses auto_ptr is a subtle beast you should read the auto_ptr docs carefully. It is also covered very well in Effective STL by Scott Meyers.