C++11

summary of more useful features of C++11

auto now can deduce type from an expression:
int i = 4;
auto j = i;
Uniform initialization:

This is a generalized extension of old C array initializer syntax. {} can define for zero, one, or more elements.

New behavior:
- Prevents any narrow conversions.
- An empty {} uses default initialization (0 for ints).

std::vector<int> numbers = { 1, 2, 3, 4 };
std::vector<int> numbers { 1, 2, 3, 4 };

A constructor can be passed an "initializer list". This snippet shows power and terseness of C++11.

Object( std::initializer_list<int> argList )
{
    for ( auto arg : argList )
}
Prefer writing {} instead of () to construct an object:
Object obj();  // PITFALL!  Actually a function declaration!
Object obj{};

Another benefit is that {} prevents narrowing conversions (truncations).

Range for loops:

General syntax is: for ( type var : list )

std::vector<Object> objects { Object{1}, Object{2}, Object{3} };
for ( Object& object : objects )
Declaring constants in classes, class constant values vs class const objects:

Declaring constants in classes isn't intuitive. Writing const ought to work but it won't. Only improvement C++11 offers is ability to declare float constants in classes (instead of #define kludge) but new syntax is even more arcane. Which syntax to use depends on if a constant is a simple literal value or a read-only object.

Use static constexpr (CLASS_CONSTEXPR macro) for constant values of basic types (symbolic constants).

Use static const (CLASS_CONST macro) for read-only higher-level objects (beyond basic types).

#define CLASS_CONST     static const      // use for a class object that is const
#define CLASS_CONSTEXPR static constexpr  // use for a literal constant of a basic type

class Class
{
    static constexpr float CONST_FP = 1.2345f;  // symbolic constant value of basic type
    CLASS_CONSTEXPR        CONST_FP = 1.2345f;

    static const std::vector<Class> mReadOnlyData;  // read-only high-level object
    CLASS_CONST  std::vector<Class> mReadOnlyData;
};
constexpr:

constexpr tells compiler that an expression must be evalutable at compile-time. A function can be declared constexpr (practically, it should be just a return statement returning constant or constexpr).

nullptr:

Although null pointers should be eliminated whenever possible. nullptr won't be misinterpreted as a int or long like NULL could.

Declaring underlying type of an enum is now possible:

enum : unsigned int { RR, GG, BB, AA };

Scoped enums ("enumeration class"):

New behavior of "enum class":
- Enums within a separate enum scope.
- Every enum is "strongly typed", implicit conversions to int are forbidden.
- Optionally can specify underlying integer type using : type
- Can be forward-declared: enum class ETrafficLight;
- Underlying type is availabe as: std::underlying_type<ETrafficLight>::type

enum class ETrafficLight { RED, YELLOW, GREEN };
enum class ETrafficLight : int { RED, YELLOW, GREEN };   // default underlying type is int
enum class ETrafficLight : char { RED, YELLOW, GREEN };
override:

Suggesting habit of writing override. override tells compiler that a virtual method is supposed to override a base one. If it doesn't override anything, compiler reports an error. It catches mistake of a method having same name but a slightly different type, so that it incorrectly would define a new method instead of overriding an existing one. Also it can catch when a method was declared virtual but doesn't need to be virtual. override can be applied to overloaded methods, as long as a matching overload is found.

final:

final has two behaviors:
- prevents deriving from a final class.
- prevents overriding a final virtual method.

class FinalDerivative final : public Base   // final class
virtual void FinalMethod( void ) override final;  // final virtual method, should write override and final as a pair
default:

A C++ rule forbids compiler auto-generating a default constructor if a special constructor was written. C++11 has a new declaration syntax to tell compiler to auto-generate a constructor.

Writing = default to declare an auto-generated default constructor is recommended if possible, which avoids mistake of forgetting to copy a particular member.

class Object
{
    Object( const std::string& name );                 // special constructor otherwise inhibits auto-generation of other ctors
    Object( void ) = default;                          // tell compiler to auto-generate default constructor, otherwise it won't
    Object( const Object& src ) = default;             // tell compiler to auto-generate copy constructor, otherwise it won't
    Object& operator=( const Object& src ) = default;  // tell compiler to auto-generate assignment operator, otherwise it won't
};
delete:

delete now can be used to disable a method. This is useful to prevent copying an object that isn't copyable.

Object( const Object& ) = delete;
Object& operator=( const Object& ) = delete;
Inheriting constructors:

(C++ was originally designed so that constructors weren't inherited. Rationale was that a new constructor is usually necessary.)

Be aware this becomes a PITFALL if derived class has new members. By writing using, compiler can be told to bring base constructors into context of derived class:

class Derived : public Base
{
    using Base::Base;  // inherit all base constructors
};
decltype(), suffix return type:

decltype() means type of an expression. Expression can be a potential expression, an expression that isn't really executed.

suffix return type is a new syntax using combination auto and -> with a new meaning:
auto Func() -> returnType;

In practice, this is useful in template functions, using decltype() for return type. In following example, notice that expression (x*y) is an expression that isn't really compiled. decltype() is useful because it returns type of sum (which may involve type promotion), without knowing in advance types. Without this new syntax, would have to resort to hard-coding fpx (extended double) to avoid precision loss.

template<typename FP1,typename FP2>
auto Product( const FP1 x, const FP2 y ) -> decltype(x*y)
{
}
How to use a lambda in a constructor/ctor to initialize a member:
public: Object2( void )
:   mObj( [](){ ......... code ...........; return obj; }() )
          |<-------------- lamba definition ----------->|^^ function call syntax to call lambda

C++ tricks

Type signatures:
Type signatures can automatically catch memory corruption problems. Otherwise, such problems are among most difficult to track down using a debugger. A type signature is an additional member in a data struct that holds a unique integer which identifies type. Functions that are passed a pointer to a data struct can assert on its type signature to ensure data struct is of expected type and its memory wasn't corrupted. Type signatures can be implemented as C macros which only expand in debug builds (and thus won't incur any overhead in release builds).
To turn a fundamental type into a (somewhat) distinct type:
#define DECLARE_DISTINCT_TYPE( CLASS, T )                           \
class CLASS                                                         \
{                                                                   \
public:                                                             \
    CLASS( T val ) : mVal(val) { }                                  \
    operator T() const { return mVal; }                             \
    CLASS& operator=( const T& val ) { mVal = val; return *this; }  \
private:                                                            \
    T mVal;                                                         \
};
DECLARE_DISTINCT_TYPE( Radian, float )
DECLARE_DISTINCT_TYPE( Degree, float )
void Rotate( Radian rad );  // these overloaded functions become possible
void Rotate( Degree deg );  // yet can be manipulated as floats

STL code snippets

// Vector:
vector<string> vec;
vec.push_back( name );      // push_back() means append
vec.clear();

// Iterating thru a container:
vector<string> files;
vector<string>::iterator itr;
for ( itr = files.begin(); itr != files.end(); ++itr )
    std::cout << *itr << endl;

// Iterating by subscript using an index (N/A to associative containers):
vector<MyClass> vec;
for ( int i = 0; i < vec.size(); ++i )

// Building and iterating thru a map and using pairs:
map<string,int> mp;
mp.insert( pair<string,int>( "a", 1 ) );
mp.insert( pair<string,int>( "b", 2 ) );
mp.insert( pair<string,int>( "c", 3 ) );
map<string,int>::iterator itr;
for ( itr = mp.begin(); itr != mp.end(); ++itr )
    std::cout << itr->first << " " << itr->second << endl;

// To test if a key exists in a map/set:
if ( mMap.find(key) != mMap.end() )

// Adding then removing from a container.
list<int> l;
for ( int i = 0; i < 10; ++i )
    l.push_back( i );  // append
while ( ! l.empty() )
{   // Prints/pops oldest (head) element first,
    std::cout << l.front() << endl;
    l.pop_front();
}

// If no match.
if ( map.find("key") == map.end() ) std::cout << "not found" << endl;

// Print a container.
list<string> con;
copy( con.begin(), con.end(), ostream_iterator<string>(std::cout," ") );

// Copy a container:
copy( con1.begin(), con1.end(),
      con2 );                // WRONG/PITFALL if con2 smaller than con1

con2.clear();
copy( con1.begin(), con1.end(),
      back_inserter(con2) ); // OK if con2 smaller than con1

// Sorting using a binary predicate:
// An alternative (using container container pointers instead
// of values) that doesn't need a binary predicate is to define
// user's own operator<() which sort() uses by default.
bool BinPred( const Class& o1, const Class& o2 )
{
    return o1.val < o2.val;
}
vector<Class> vec;
sort( vec.begin(), vec.end(), BinPred );

// A function that returns a pair.
// Pair is returned by value (like a struct would be, so it isn't dangling).
std::pair<float,float> AddMul( float x, float y )
{
    std::pair<float, float> res;
    res.first  = x + y;
    res.second = x * y;
    return res;
}

// Print a binary number in base 2 using bitset.
#include <bitset>
bitset<8> val = 0xff;
std::cout << val;

// Turn string to all upper-case.
string s;
transform( s.begin(), s.end(),
           s.end(),
           toupper );

// Distance between two iterators.
distance( itr1, itr2 )

// Getting iterator of an item that was inserted into a map:
// (a multimap/multiset differs, adapted from gfx_share.hh)
std::pair<typename Map::iterator,bool> insertion;
insertion = mMap.insert( std::make_pair( *obj, std::make_pair(copy,1) ) );
itr = insertion.first;  // insertion is assumed to succeed (bool not checked)

// Replacing/substituting chars of a string.
// string::replace() is really an overwrite, not a substitution.
char ProperDirChar( char c )
{
    return c == '\' ? '/' : c;
}
string ProperDir( const string& dirname )
{
    string s = dirname;
    std::transform( s.begin(), s.end(),
                    s.begin(),
                    ProperDirChar );
    return s;
}

stream code snippets

// Save/restore stream flags.
std::ios::fmtflags savedFlags = std::cout.flags();
...
std::cout.flags(savedFlags);

// showbase (eg automatically print "0x" prefix for hex, etc).
std::cout.setf( std::ios::showbase );

// Set float precision.
mStream.setf( ios::fixed, ios::floatfield );
mStream.precision( 20 );

// Writing to an integer to a stream using width/precision.
// Note that width/precision is discarded after every call to stream object!
std::cout << std::setw(5) << std::setfill('0') << x << ' ';
std::cout << std::setw(5) << std::setfill('0') << y << ' ';
std::cout << std::setw(5) << std::setfill('0') << z << ' ' << std::endl;

// Convert an int to a string stream for purpose
// of passing int as a C++ string.
int i;
ostringstream ss;
ss << i; Print( ss.str() );

// Open for reading to test if file is empty.
strm.open( fname, ios::in|ios::binary );
if ( strm.good() && strm.is_open() )
{
    // File exists.  Is it empty?
    strm.seekg(1);
    char c; strm >> c;  // a read is required to trigger EOF, seekg(1) alone won't
    if ( strm.eof() )
        std::cout << "File exists and is empty." << endl;
    else
        std::cout << "File exists and contains data." << endl;
}
else
{
    std::cout << "File doesn't exist." << endl;
}

// Reopen in R/W mode.
strm.close();
strm.clear();
strm.open( fname, ios::in|ios::out|ios::trunc|ios::binary );
strm.seekp(0);

redirecting streams

To write a class that redirects a stream to something else, define user's own streambuf class, then construct an ostream with pointer to streambuf object. An example is in Nicolai Josuttis's STL book.

A faux-pas is trying to derive from std::ostream. One problem is that passing endl will result in a bad cast exception (g++ 3/4).

http://shekel.jct.ac.il/cc-res/online-doc/libgPP/iostream_28.html
http://www.lysator.liu.se/c/bs-errata-1.html
http://gcc.gnu.org/onlinedocs/libstdc++/27_io/howto.html#6

////////////////////////////////////////////////////////////////////////////////
/// @brief NopStream is a indirect way to use a disabled C++ stream.
///
/// This is a "streambuf" class to serve as basis of an ostream.
/// Derived from Nicolai Josuttis's STL book.
///
/// @verbatim
/// Example:
///     NopStreambuf nopStreamBuf;
///     std::ostream gLog( &gNopStreambuf );
/// @endverbatim
///
class NopStreambuf : public streambuf
{
PREVENT_COPYING(NopStreambuf)
typedef streambuf Parent;
public:
    NopStreambuf( void ) { }
    ~NopStreambuf() { }

protected:
    virtual int_type    overflow( int_type c ) { return ~EOF; }
    std::streamsize     xsputn( const char* buf, std::streamsize n ) { return n; }
};

defining operator<<()

Idea is to overload operator<<() with user-defined type.
class Point
{
public:
    int x, y;
};

ostream& operator<<( ostream& strm, const Point& obj )
{
    strm << "(" << obj.x << "," << obj.y << ")";
    return strm;
}

overloaded operators

Overloaded operators, except assignment, are inherited. But subtle compiler errors can occur.

Let's say there are two related classes that are logically different types, but are structurally equivalent (identical members).

class Vertex
{
public:
    Vertex& operator+( const Vertex& src );

    float x, y, z;
};

class WorldVertex : public Vertex
{
public:
};

Now try adding two derived objects:

void Draw( const WorldVertex& v );

WorldVertex v1, v2, v3;
v3 = v1 + v2;      // compile error
Draw( v1 + v2 );   // ok

Some compilers will give obscure errors, leading one to think that one needs to duplicate all overloaded operator code into every derived class. That wouldn't be ideal.

What's happening in v3 = v1 + v2 is that Vertex::operator+() is called which returns a base Vertex object, not a derived WorldVertex object. One might think assigning a base object into a derived object is an immediate error. But C++ compiler first tries to find matching assignment operator such as Derived& operator=( const Base& ):

class WorldVertex : public Vertex
{
public:

    WorldVertex& operator=( const Vertex& src )
    {
        x = src.x;
        y = src.y;
        z = src.z;
        return *this;
    }
};

Because these classes are structurally equivalent, Derived& operator=( const Base& ) does make sense, as it can simply copy .x, .y, .z.


C++ pitfalls, traps

delete vs delete[]:

This author solved this and all other memory problems by developing two smart-pointers. SharedPtr is a reference-count smart-pointer with reference-count intrusively stored in objects by deriving from Shared base class. (Boost library offers boost::intrusive_ptr.) SafePtr is a smart-pointer template class with private delete operator to prevent deleting.

Never write a virtual const method. Avoid const methods.
class Base
{
    virtual bool IfLoaded( void ) const;
};

class Derived
{
    virtual bool IfLoaded( void );  // OOPS! forgot const
};

void Func( const Base& obj )  // pitfall opens here
{
    if ( obj.IfLoaded() )  // calls Base::IfLoaded() despite Derived class
}

What happens is that Derived::IfLoaded() DOES NOT OVERRIDE base method. Rather, Derived::IfLoaded() BECOMES A DIFFERENT/DISTINCT METHOD!

Reason is that they are methods with different types (const vs. non-const), so C++ treats them as two different/distinct methods, rather than one polymorphic method.

A programmer can too easily forget if a virtual method should be const or non-const. Advice is to avoid any kind const method (except maybe in basic self-contained classes).

Reusing or misusing assignment operator=() in a copy constructor:

This pitfall exists with more complex classes whose members aren't fundamental types. Assignment operators should free members before reassigning them. If an operator=() that frees resources is called in a copy constructor, it will try to free garbage. A solution is have separate Copy() and Free() methods.

class Class
{
public:
    Class( const Class& src )
    {
       *this = src;      // temptation to write terse code leads to a pitfall
    }
    Class& operator=( const Class& src )
    {
        delete mObj;     // free members
        mObj = src.Obj;  // reassign members
        return *this;
    }
private:
    Class2*   mObj;
};
Method overriding will fail if one forgets to write virtual by a method in base class or function signatures differ.
 
Calling a virtual method from a base constructor:

Think about order of construction: derived object hasn't been constructed yet.

Default copy constructors or assignment operators may cause trouble.

Safe-guards writing a dummy default copy constructor as private and/or with assert(false).

A constructor with a single arg might be misinterpreted as a conversion operator.

Write explicit if conversion isn't desired.

Class( int );
int n;
Class obj = n; // converts an int to a Class obj !!
Temporary objects and reference args:
void Byref( int& x )
{
    ...
    x = y;
}
Byref( a + 2 );   // oops, result went nowhere into a temp
operator bool():

operator bool() is seductive for tersely testing if an object is valid:

class Data
{
public:
    operator bool() const { return mValid; }
private:
    string mData;
};

void Process( Data& data )
{
    // Valid data?
    if ( data )
    {
    }
}

Let's say two objects are valid but their values (members) differ.

if ( data0 == data1 ) return;

One would think return won't happen. But it will. This is what's compiled:

if ( bool(data0) == bool(data1) ) return;  // true == true

Pitfall is implicit conversion. Class doesn't define operator==(). But compiler doesn't supply a default memberwise comparison as might be assumed. Rather, compiler implicity converts both operands to bools. Because that's precisely what operator bool() is for.

If a C++11 compiler is available, prefer writing {} instead of () to construct an object:
Object obj();  // PITFALL!  Actually a function declaration!
Object obj{};

Another benefit is that {} prevents narrowing conversions (truncations).


STL pitfalls, traps

end():
for ( itr = files.begin(); itr < files.end(); ++itr )   // WRONG
for ( itr = files.begin(); itr != files.end(); ++itr )  // ok
reserve() vs resize():

resize() expands container -- reserve() doesn't!

Incrementing iterators:
while ( itr++ != files.end() )  // WRONG, itr incremented past end
Short destination containers:

This could be a bug if vec2 is shorter than vec. copy() won't extend destination container.

copy( vec.begin(), vec.end(),
      vec2.begin() );

One solution is: vec2.resize( vec1.size() ). Another is to use an insert iterator (insertor):

copy( vec.begin(), vec.end(),
      back_inserter(vec2) );
Catenating a string with a char:
char baseName[] = "myfile";
string suffix = "txt";
string fileName = baseName + '.' + suffix;   // WRONG

Above is wrong because compiler misinterprets this as C's way of adding an integer to a pointer rather than as C++ string catenation. That is, compiler's intepretation is:

string fileName = &baseName[ int('.') ] + suffix;
Auto-insertion of missing key into STL map:
if ( mMap[key] == needle )

Above is wrong because map::operator[] will automatically insert a missing key!

if ( mMap.find(key) != mMap.end()
  && mMap[key] == needle )

iterating generically across containers of directly-stored objects or pointers

A problem is when there are two different types of contains, one directly stores objects, other stores pointers. Then a common template function is needed to process both types, but a problem appears when accessing object via iterator:

typedef std::vector<Object> Objects;
typedef std::vector<Object>* ObjectPtrs;

Object* obj = &(*iter);  // container that stores objects directly
Object* obj = *iter;     // container that stores pointers to objects

A solution is a wrapper class which gets object as a pointer, allowing template function to be just written in terms of pointers.

////////////////////////////////////////////////////////////////////////////////
/// @brief Wrapper class to get an object from an iterator as a pointer.
///
template<class OBJECT_DIRECT, class OBJECT_CONTAINER>
class IteratorToPtr
{
    public: typedef OBJECT_DIRECT* Pointer;
    public: static OBJECT_DIRECT* Get( typename OBJECT_CONTAINER::iterator iter ) { return *iter; }
};

////////////////////////////////////////////////////////////////////////////////
/// @brief Wrapper class to get an object from an iterator as a pointer.
///
template<class OBJECT_DIRECT, class OBJECT_CONTAINER>
class IteratorToPtrByAddress
{
    public: typedef OBJECT_DIRECT* Pointer;
    public: static OBJECT_DIRECT* Get( typename OBJECT_CONTAINER::iterator iter ) { return &(*iter); }
};

/*******************************************************************************
 * 
 *******************************************************************************/
template<class OBJECT, class OBJECT_CONTAINER, class ITERATOR_TO_PTR>
OBJECT* FindObject( int val, OBJECT_CONTAINER& objects )
{
    for ( typename OBJECT_CONTAINER::iterator iter = objects.begin();
          iter != objects.end();
          ++iter )
    {
        typename ITERATOR_TO_PTR::Pointer object = ITERATOR_TO_PTR::Get( iter );
        if ( object->mVal == val )
            return object;
    }
    return Object::Dummy();
}

commentary/opinion

[2023/05]

summary

Overall, this author still has a positive opinion of C++ and shall continue writing C++.

positive opinion

C++ is the best of the practical/pragmatic programming languages.
This statement does NOT mean C++ is excellent -- it means the alternatives are worse.

C++ became really good with the radical improvements of C++11.

negative opinion

C++ has become way, way, too big. C++23 standard is beyond comprehension. The syntax of C++23 is so complicated, convoluted, incoherent (and broken down, some say) that no programmer can fully trust any compiler to compile every possible syntax combination 100% correctly, hence the stay on the proven subset of C++ rule. (This author does know similar doubts against ALGOL-68 later proved false.) (FORTH compilers were 100% reliable, had ZERO bugs, because of being so tiny.)

"Look at today's situation, people are programming in C++ -- THE WORST DISEASE EVER CREATED! (laughter) ...[C++, Java, C#] They all suffer from their mightiness. I'm always expecting them to collapse under their own weight."
[Niklaus Wirth, ACM video]

every mutant of C++ has failed

Several mutants of C++ were hyped as the programming language that would replace C++. Fools tried to "correct" C++ by creating imitations of C++ but each was much worse than C++. Even Dr. Frankenstein could not have made a worse abomination than the one Sun Microsystems hacked together. Popularity of each C++ mutant among novices spread, until the novices became experienced and frustrated with their limitations, then each C++ mutant was dumped as trash and forgotten.

D (dlang) merits consideration, nevertheless. D was integrated into GNU gcc-9 in 2019. Code of its reference compiler is readable and written in itself -- D is written in D. But too much expressiveness of C++ was lost by trying to "improve" syntax of C++. Forex, smart-pointers are impossible in D, because D restricts operator-overloading to arithmetic [2023/06].

stay on the proven subset of C++

Because of the complicated syntax of C++23 with all its permutations, no programmer can fully trust any compiler to compile C++23 code 100% correctly. Programmers should avoid the arcane pretzel-logic areas of syntax of C++23, should limit themselves to writing in the proven subset of C++ syntax, never venturing beyond essential improvements of C++11.