Articles & Books

Folds(ish) in C++11

Folds of variadic expressions are coming in C++17, but what about today?

Folds(ish) in C++11

by Jason Turner

From the article:

It is possible to perform some form of the variadic folds that are coming in C++17 with the C++11 and C++14 compilers that we have available today by using just a little bit of creativity.

...

Someone (not me) figured out that we can abuse the std::initializer_list type to let us guarantee the order of execution of some statements while not having to recursively instantiate templates. For this to work we need to create some temporary object to let us use the braced initializer syntax and hold some result values for us.

Only problem is, our print function doesn’t return a result value. So what do we do? Give it a dummy return value!

#include <iostream>

template<typename T>
void print(const T &t)
{
  std::cout << t << '\n';
}

template<typename ... T>
  void print(const T& ... t)
{
  std::initializer_list<int>{ (print(t), 0)... };
}

int main()
{
  print(1, 2, 3.4, "Hello World");
}

...

(And we continue to simplify it much more from here...)

Quick Q: std::rethrow_exception and thrown exception type

Quick A: std::make_exception_ptr creates a reference on a copy.

Recently on SO:

std::rethrow_exception and thrown exception type

From documentation for std::make_exception_ptr:

Creates an std::exception_ptr that holds a reference to a copy of e.

Unfortunately, copying e means you get object slicing (which @Mohamad Elghawi points out is also more prominently mentioned later on that page). When you call std::make_exception_ptr<RecognitionException>, it will hold a copy of a RecognitionException, not any derived class.

But you don't need exception_ptr at all here. Even though reportError does not have the try...catch in scope, you can still use throw; to re-throw the current exception.

#include <stdio.h>

struct A { virtual ~A() = default; };
struct B : A { };

void reportError() {
  try {
    throw;
  }
  catch (B &) {
    puts("caught B");
  }
  catch (A &) {
    puts("caught A");
  }
}

int main() {
  try {
    throw B();
  }
  catch (A &) {
    reportError();
  }
}

Quick Q: Static constexpr int vs old-fashioned enum: when and why?

Quick A: A static constexpr cannot be used in an ODR context.

Recently on SO:

Static constexpr int vs old-fashioned enum: when and why?

There will be no noticeable difference for integral constants when used like this.

However, enum is actually better, because it is a true named constant. constexpr integral constant is an object which can be, for example, ODR-used - and that would result in linking errors.

#include <iostream>

struct T {
    static constexpr int i = 42;
    enum : int {x = 42};
};

void check(const int& z) {
    std::cout << "Check: " << z << "\n";
}

int main() {
    // check(T::i); // Uncommenting this will lead to link error
    check(T::x);
}

When check(T::i) is uncommented, the program can not be linked:

/tmp/ccZoETx7.o: In function `main': ccc.cpp:(.text+0x45): undefined reference to `T::i' collect2: error: ld returned 1 exit status

However, the true enum always works.

Diagnosable validity -- Andrzej KrzemieĊ„ski

Andrzej Krzemieński wrote down his thoughts on ill-formed C++ code.


Diagnosable validity

by Andrzej Krzemieński

From the article:

Certain combinations of types and expressions can make a C++ program ill-formed. “Ill-formed” is a term taken from the C++ Standard and it means that a program is not valid, and compiler must (in most of the cases) reject it. This is quite obvious:

int main()
{
  auto i = "some text".size(); // invalid expression
};

String literals do not have member functions, therefore compiler cannot accept this program, and must report an error. This puts a responsibility on programmers to learn which expressions and types are valid in a given context and use only these. Again, I am saying a very obvious thing.

What is less obvious is that there is a way in C++ to enter a type or expression of which we do not know if it is valid or not, in an isolated environment, where it does not render the entire program ill-formed, but instead it returns a yes-no (or rather valid-invalid) answer, which we can use at compile-time to make a decision how we want the program to behave. When requested, compiler can analyze all the declarations it has seen so far, and make an approximated judgement whether a given type or expression would make the program ill-formed or not, if used outside the isolated environment. The compiler’s approximated answer is not always correct, but it is just enough most of the time.

 

Quick Q: Templated Function results in Circular Inclusion

Quick A: Separate definition and implementation.

Recently on SO:

Templated Function results in Circular Inclusion

Define registerEvent after IApp.

class IApp;

class Component
{
    IApp* app;
    template<typename T>
    void registerEvent(const int& evtId, Status (T::*func) (int));
};

class IApp : public Component {
  ...
};

template <typename T>
Component::registerEvent(const int& evtId, Status (T::*func) (int)) {
  auto res = std::bind(func, (T*)this, std::placeholders::_1);
  app->registerForEvent(evtId);
}

If need be, also define A::registerEvent after Component::registerEvent.

Quick Q: Why is list initialization (using curly braces) better than the alternatives?

Quick A: It is less likely to generate an unexpected error.

Recently on SO:

Why is list initialization (using curly braces) better than the alternatives?

Basically copying and pasting from Bjarne Stroustrup's "The C++ Programming Language 4th Edition":

List initialization does not allow narrowing (§iso.8.5.4). That is:

  • An integer cannot be converted to another integer that cannot hold its value. For example, char to int is allowed, but not int to char.
  • A floating-point value cannot be converted to another floating-point type that cannot hold its value. For example, float to double is allowed, but not double to float.
  • A floating-point value cannot be converted to an integer type.
  • An integer value cannot be converted to a floating-point type.

Example:

void fun(double val, int val2) {

    int x2 = val; // if val==7.9, x2 becomes 7 (bad)

    char c2 = val2; // if val2==1025, c2 becomes 1 (bad)

    int x3 {val}; // error: possible truncation (good)

    char c3 {val2}; // error: possible narrowing (good)

    char c4 {24}; // OK: 24 can be represented exactly as a char (good)

    char c5 {264}; // error (assuming 8-bit chars): 264 cannot be
                   // represented as a char (good)

    int x4 {2.0}; // error: no double to int value conversion (good)

}

The only situation where = is preferred over {} is when using auto keyword to get the type determined by the initializer.

Example:

auto z1 {99}; // z1 is an initializer_list<int>
auto z2 = 99; // z2 is an int

Conclusion

Prefer {} initialization over alternatives unless you have a strong reason not to.

Top 10 dumb mistakes to avoid with C++ 11 smart pointers -- Deb Haldar

Discussion on various aspect of C++11 smart pointers uses.

Top 10 dumb mistakes to avoid with C++ 11 smart pointers

by  Deb Haldar

From the article:

I love the new C++ 11 smart pointers. In many ways, they were a godsent for many folks who hate managing their own memory. In my opinion, it made teaching C++ to newcomers much easier.

However, in the two plus years that I've been using them extensively, I've come across multiple cases where improper use of the C++ 11 smart pointers made the program inefficient or simply crash and burn. I've catalogued them below for easy reference.

Quick Q: Is this std::ref behaviour logical?

Quick A: Yes, std::ref can be reassigned (see example)

Recently on SO:

Is this std::ref behaviour logical?

A small modification to f2 provides the clue:

template<class T>
void f2(T arg)
{
    arg.get() = xx;
}

This now does what you expect.

This has happened because std::ref returns a std::reference_wrapper<> object. The assignment operator of which rebinds the wrapper. (see http://en.cppreference.com/w/cpp/utility/functional/reference_wrapper/operator%3D)

It does not make an assignment to the wrapped reference.

In the f1 case, all is working as you expected because a std::reference_wrapper<T> provides a conversion operator to T&, which will bind to the implicit right hand side of ints implicit operator+.

Strict Weak Ordering and STL -- Saurabh Singh

Saurabh Singh describes in a brief tutorial on how to correctly implement the comparator function for STL containers and algorithms. 

Strict Weak Ordering and STL

by Saurabh Singh

From the article:

If you had ever used a map or set or even std::sort I bet you would have to give a comparator function. (Or an overload to the < (less than) operator).
I will try to give an overview of how certain associative stl containers use this property for ordering the elements.
Almost all stl containers rely on strict weak ordering A strict weak ordering defines the relative position of elements in terms of precedence of one item over other. For eg. if you have a room full of person and you have to form a queue based on their height, a person with "lesser" height will "precede" the person with greater height. For a function to be satisfying strict weak ordering following conditions need to be met.

The PDF version of the document is available here.