std::ignore – Elegant solution to a simple problem

If you love tuple (you probably do already), there’s a clever way to have clean code, similar to python, to pass parameters for which you don’t actually care to have the value to. To restate that, an easy way to extract only 1 or 2 of the values from a multi-value tuple and ignoring the rest. A common example is with inserting a value into a set. If you want to verify if a value is already in the set, a common idiom is to insert the value in the set, and verify the return value to determine if the value was in fact inserted. If the value was inserted, then the value wasn’t already in the set. Mystery solved. Note: I use a pair in this example instead of tuple. Same idea.

First, let’s look at a lengthier implementation without using std::ignore:

std::set<std::string> mySet;

// insert some values in the set...

// return type is a pair of < iterator, bool >
using SetRetType = 
   pair<std::set<std::string>::iterator, bool>;
	
SetRetType retVal = mySet.insert("Value");
if (retVal.second)
{
	// New value inserted, do something...
}
Continue readingstd::ignore – Elegant solution to a simple problem

Runtime vs Compilation speed – The new deal

Years ago on many projects we would gladly accept a longer compile time for a faster execution at runtime. However with the acceptance of continuous integration gaining momentum, it’s not “always” a clearly acceptable trade-off. With more frequent code submissions, it’s become desirable to compile and test each submission as independently as possible. This (ideally) allows any compilation errors or test failures to be rapidly traceable to the rogue coder’s submission. Now the trade-off hasn’t just changed. It’s mostly gone. We need to both have fast compile times, and blazing execution. Luckily, this is where judicious software design can help.

The idea of keyword inline is to speed up code execution by inserting the definition of the inlined function directly a the site of the function call. You can read about in the standard [dcl.inline / 10.1.6]. For a function definition of a single line, we can see how avoiding a function call can be beneficial. That single line of code gets inserted directly into the calling code. But, there a plenty of reasons why you mostly don’t want to use inline, unless that function to be inlined is very simple and short as described above.

Continue readingRuntime vs Compilation speed – The new deal

default – it’s not (just) syntactic sugar

One of the arguably more obvious features introduced in C++11 is the ability to specify keyword default for special member functions. The default keyword instructs the compiler to define the function as if it implicitly would have. In the case of a copy constructor and a POD class, the compiler would define a function to copy each member. The main reason you’d want to do this yourself, as opposed to letting the compiler implicitly declare and define it for you, is if the compiler refuses to do so. For example, if there’s a move operation declared in the class. This is based on the rule of Three (now rule of Five). The idea is that if you declare you own version of the move operator for the class, the default copy constructor may not be appropriate. But this may be old news so let’s move on.

Continue readingdefault – it’s not (just) syntactic sugar

Incomplete types and where to find them

An incomplete type is a type that has been declared, but its definition hasn’t been seen by the compiler. From the standard [Types / 3.9 note 5]:

“A class that has been declared but not defined, an enumeration type in certain contexts (7.2), or an array of unknown size or of incomplete element type, is an incompletely-defined object type.45 Incompletely-defined object types and the void types are incomplete types (3.9.1). Objects shall not be defined to have an incomplete type.”

To summarize, you can’t define an object of an incomplete type (or dereference a pointer to one). Footnote 45 in the quotation from the standard above gives a hint as to why:

“The size and layout of an instance of an incompletely-defined object type is unknown.”

The compiler knows to allocate enough space for a pointer type, an address in memory, but wouldn’t know how much space to allocate for the incomplete type since its size is yet to be defined.

Probably the most famous incomplete type is void. Void is a fundamental type which is used in a few specific tasks, such as declaring a function which doesn’t return a value, or a parameterless function:
Continue readingIncomplete types and where to find them

Return value optimization (copy elision)

Now that most code bases are using modern (post C++11) compilers, it’s common to encounter the move semantic judiciously used throughout. Implicitly or explicitly. Which brings us to return value optimization (RVO), which the standard refers to as copy elision [Copying and moving class objects / 12.8.31].

“ … the implementation treats the source and target of the omitted copy/move
operation as simply two different ways of referring to the same object, and the destruction of that object occurs at the later of the times when the two objects would have been destroyed without the optimization.”

In brief, RVO is an optimization (compiler isn’t required to, but the fallback isn’t bad either) where the return value is constructed directly into the returned value location. Meaning, you avoid all copying. I say all, since no conversion can take place since for the RVO to considered by the compiler, the return type and the returned to object must be the same type. Basically, this:
Continue readingReturn value optimization (copy elision)

Order of evalution in method chaining

This post is related to my earlier one. Expanding on the subject of the order of evalutions, let’s start with an example:


class A
{
public :
    A() : _myValue(0) {};

    A& IncrVarTen()
    {
        _myValue += 10;
        cout << "value is:" << _myValue << endl;
        return *this;
    }

    A& IncrVarTwenty()
    {
        _myValue += 20;
        cout << "value is:" << _myValue << endl;
        return *this;
    }

    int _myValue;
};


int main()
{
    A a;
    a.IncrVarTen().IncrVarTwenty();
    cout << "Final value is:" << a._myValue << endl; 
    return 0;
}

Continue readingOrder of evalution in method chaining

Non-type template arguments

The usual method to employ templates is to supply a type to its parameters. Most C++ beginners at some point have used container, and supplied the value type of the container (and possibly more types for the other parameters such as an allocator type, which are in fact template template arguments).

Less common (in my experience) are nontype template arguments. A simple definition would look like this:

template <typename T, T nontypeParam>
class A
{
...
};

A<int,10> a;

There is also the option of specifying the type directly in the definition:
Continue readingNon-type template arguments

Modern C++ and backwards compatibility for keyword auto

With the introduction of keyword auto to implement type deduction, ever wonder why this “new” keyword didn’t break tons of old code when projects were migrated to newer compilers? Turns out keyword auto has been reserved for quite some time in the standard. Therefore it isn’t a new keyword at all, but one which was rarely used. The standard addresses this in the appendix sections and . These sections state that auto can no longer be used as a storage class specifier. The rationale given for this change, is basically that the keyword is being re-purposed for better use.

What was the original use of auto? It was to be used to define automatic storage duration of local variable within a block. The block could be a function or just a block of code. This means the local variables are created in the block, and cease to exist at the end of the block. For example:

 // Example 1
{
   // Automatic storage duration by default.
   int myVar = 0;
   …
   // Not valid code starting with C++11.
   auto int otherVar = 0; 

} // Both vars cease to exist.

Or, more interestingly:

// Example 2
std::mutex myMutex;
{
   // Automatic storage duration. 
   std::lock_guard<std::mutex> myLockGuard(myMutex);
   ...
   
} // variable  ceases to exist and  mutex is released.

As shown in the example 2 above, this automatic storage duration is a key part of RAII . The other types of storage being register, static, extern, thread_local. The fact that auto storage class is the default, doesn’t make it particularly useful. Therefore, it’s easy to see why having a name like “auto” it was recycled to it’s new purpose.

Default arguments and overload resolution

Default arguments can be a surprisingly lengthy subject. What I want to cover here is more to do with how they work with declarations and inheritance. We’ll start by looking at overload resolution, then introduce default arguments, and finally virtual functions.

Overload resolution
It’s the process of selecting the best function to call based on the arguments of the function call (or the expressions which will result in the arguments) and the set of candidate function which could be called based on the call site [over.match / 13.3]. It’s a 2 step process:

1. The set of viable functions is determined based on the functions meeting the number of arguments and other criteria.
2. The best viable function is selected based on the implicit conversion rules to match each argument to the respective parameter of each viable function.

An interesting note is that accessibility is never considered. Meaning the best viable function for a call could be inaccessible, if say it was declared private. It would be considered ill-formed and your compiler should complain.
Continue readingDefault arguments and overload resolution