C++ Core Guidelines: More Non-Rules and Myths


Demystifying non-rules and myths in C++ is a laborious but absolutely necessary job. The goal is simple: use the powerful tool C++ appropriately.

 sao jorge 1503439 1280

By the way, did you noticed that my family name qualifies me, in particular, to write about this demystification? Anyway, here are the rules from the C++ core guidelines for today.

NR.5: Don’t: Don’t do substantive work in a constructor; instead use two-phase initialization

Obviously, this is the job a constructor: After the constructor is executed you should have a fully-initialised object. For that reason, the following code snippet from the guidelines is bad. 

class Picture
    int mx;
    int my;
    char * data;
    Picture(int x, int y)
        mx = x,
        my = y;
        data = nullptr;


    bool Init()
        // invariant checks
        if (mx <= 0 || my <= 0) {
            return false;
        if (data) {
            return false;
        data = (char*) malloc(x*y*sizeof(int));
        return data != nullptr;

    void Cleanup()                    // (2)
        if (data) free(data);
        data = nullptr;

Picture picture(100, 0); // not ready-to-use picture here
// this will fail..                   // (1)
if (!picture.Init()) {
    puts("Error, invalid picture");
// now have a invalid picture object instance.

picture(100, 0) is not fully initialised and, therefore, all operation on the picture in line (1) operate on an invalid picture. The solution to this problem is as simple as effective: put all initialisation into the constructor.


class Picture
    size_t mx;
    size_t my;
    vector<char> data;

    static size_t check_size(size_t s)
        // invariant check
        Expects(s > 0);
        return s;

    // even more better would be a class for a 2D Size as one single parameter
    Picture(size_t x, size_t y)
        : mx(check_size(x))
        , my(check_size(y))
        // now we know x and y have a valid size
        , data(mx * my * sizeof(int)) // will throw std::bad_alloc on error
        // picture is ready-to-use
    // compiler generated dtor does the job. (also see C.21)


Additionally, data is in the second example a std::vector and not a raw pointer. This means the Cleanup function (line 2) from the first example is not necessary anymore because the compiler will automatically clean up. Thanks to the static function check_size, the constructor can validate its arguments. But this is not the end of the benefits modern C++ gives up. 

Often you use constructors to set the default behaviour of an object. Don't do it. Directly set the default behaviour of an object in the class body. For example, compare the following classes Widget and WidgetImpro.


// classMemberInitialiserWidget.cpp

#include <iostream>

class Widget{
    Widget(): width(640), height(480), frame(false), visible(true) {}
    explicit Widget(int w): width(w), height(getHeight(w)), frame(false), visible(true){}
    Widget(int w, int h): width(w), height(h), frame(false), visible(true){}

    void show(){ std::cout << std::boolalpha << width << "x" << height
                           << ", frame: " << frame << ", visible: " << visible
                           << std::endl;
    int getHeight(int w){ return w*3/4; }
    int width;
    int height;
    bool frame;
    bool visible;

class WidgetImpro{
    explicit WidgetImpro(int w): width(w), height(getHeight(w)){}
    WidgetImpro(int w, int h): width(w), height(h){}

                           << std::endl;

    int width = 640;
    int height = 480;
    bool frame = false;
    bool visible = true;

int main(){

  std::cout << std::endl;

  Widget wVGA;
  Widget wSVGA(800);
  Widget wHD(1280, 720);


  std::cout << std::endl;

  WidgetImpro wImproVGA;
  WidgetImpro wImproSVGA(800);
  WidgetImpro wImproHD(1280, 720);


  std::cout << std::endl;



Both classes behave the same.


The difference is that the constructors for the class WidgetImpro are way more comfortable to use and to extend. When you add a new variable to both classes, you have in the case of WidgetImpro only to edit one place, but each constructor in the case of the class Widget class is affected. Here is the picture I have in mind when I design a new class: Define the default behaviour of each object in the class body. Use explicit constructors to vary the default behaviour.

Done? No!

Often you use an init function to put common initialisation or validation stuff into one place. Fine, you follow the important DRY (Don't Repeat Yourself) principle, but you automatically break the other important principle, that you object should be fully initialised after the constructor call. How can you solve this riddle? Quite easy. Since C++11 we have constructor delegation. This means to put the common initialisation and validation stuff into one smart constructor and use the other constructors as kind of wrapper-constructors. Here is my idea translated to code.


// constructorDelegation.cpp

#include <cmath>
#include <iostream>

class Degree{
  explicit Degree(int deg){                // (2)
    degree = deg % 360;
    if (degree < 0) degree += 360;
  Degree() = default;
                                          // (3)
  explicit Degree(double deg):Degree(static_cast<int>(ceil(deg))) {}  

  int getDegree() const { return degree; }

  int degree{};                           // (1)

int main(){

  std::cout << std::endl;

  Degree degree;
  Degree degree10(10);
  Degree degree45(45);
  Degree degreeMinus315(-315);
  Degree degree405(405);
  Degree degree44(44.45);

  std::cout << "Degree(): " << degree.getDegree() << std::endl;
  std::cout << "Degree(10): " << degree10.getDegree() << std::endl;
  std::cout << "Degree(45): " << degree45.getDegree() << std::endl;
  std::cout << "Degree(-315): " << degreeMinus315.getDegree() << std::endl;
  std::cout << "Degree(405): " << degree405.getDegree() << std::endl;
  std::cout << "Degree(44.45): " << degree44.getDegree() << std::endl;

  std::cout << std::endl;



The expression int degree{} (line) 1 value-initialises the degree to 0. The constructor in line 2 is quite smart. It transforms each degree to the unit circle. The constructor, taking a double, uses this constructor. For completeness, here is the output of the program:


NR.6: Don’t: Place all cleanup actions at the end of a function and goto exit

Okay, we can do better as the following code from the guidelines:


void do_something(int n)
    if (n < 100) goto exit;
    // ...
    int* p = (int*) malloc(n);
    // ...


By the way. Do you spot the error? The jump goto exit bypasses the definition of the pointer p.

What I often saw in legacy C-code was code structures like this.

// lifecycle.c

#include <stdio.h>
void initDevice(const char* mess){ printf("\n\nINIT: %s\n",mess); } void work(const char* mess){ printf("WORKING: %s",mess); } void shutDownDevice(const char* mess){ printf("\nSHUT DOWN: %s\n\n",mess); } int main(void){ initDevice("DEVICE 1"); work("DEVICE1"); { initDevice("DEVICE 2"); work("DEVICE2"); shutDownDevice("DEVICE 2"); } work("DEVICE 1"); shutDownDevice("DEVICE 1"); return 0; }


This is very error-prone but also typical code. Each usage of the device consists of three steps: initialisation, usage, and release of the device. Honestly, this is the job of RAII.


// lifecycle.cpp

#include <iostream>
#include <string> class Device{ private: const std::string resource; public: Device(const std::string& res):resource(res){ std::cout << "\nINIT: " << resource << ".\n"; } void work() const { std::cout << "WORKING: " << resource << std::endl; } ~Device(){ std::cout << "SHUT DOWN: "<< resource << ".\n\n"; } }; int main(){ Device resGuard1{"DEVICE 1"}; resGuard1.work(); { Device resGuard2{"DEVICE 2"}; resGuard2.work(); } resGuard1.work(); }


Initialise the resource in the constructor and release it in the destructor. First, you can not forget to initialise the object, and secont, the compiler takes care of the release of the resource. The output of both programs is equivalent:



You can find more information to RAII in my previous post: C++ Core Guidelines: When RAII breaks.

More Myths

I'm sure, this is not the end of the fight and you know more non-rules and myths about C++. Please write a letter to This email address is being protected from spambots. You need JavaScript enabled to view it.. Describe the myth and present if possible your solution. I try to make a post out of your content and add - if you like it - your name. I'm totally curious about your ideas.

What's next

Only one rule to non-rules and myths is left in the C++ core guidelines. I hope for you input.





My special thanks to Embarcadero CBUIDER STUDIO FINAL ICONS 1024 Small




Bookable (Online)



Standard Seminars 



Contact Me

Modernes C++,





My Newest E-Books

Course: Modern C++ Concurrency in Practice


Course: Embedded Programming with Modern C++

Course: Generic Programming (Templates)

Course: C++ Fundamentals for Professionals

Subscribe to the newsletter (+ pdf bundle)

Blog archive

Source Code


Today 3600

Yesterday 5600

Week 47712

Month 73338

All 6094402

Currently are 205 guests and no members online

Kubik-Rubik Joomla! Extensions

Latest comments