A Tour of Rare C++ Features 1 of 7: Trailing Return Types, decltype and Multicharacter Literals

C++ is a treasure trove of entertaining, fascinating and just plain weird features. While most of them have sound reasons, rules like that "main my not be called by the program" feel rather arbitrary. Add to that, that there is a lot of rules and features, and some of them will fall through the cracks.

As there are very real reasons not to use many of the features I am going to present, I have made a small example that demonstrates the result of what can happen if you enjoy this kind of activity too much:

/* This is valid C++ */  
auto main() -> decltype('O.o') try  
<%[O_O = 0b0]<%
https://gha.st/a-tour-of-rare-cpp-features/  
typedef struct o O;  
o*(*((&&o(o*o))<:'o':>))(o*o);  
if(O*O = decltype(0'0[o(0)](0))(0)) 1,000.00;  
else return 0==O==0 ? throw O_O : O_O;  
%>();%>
catch(...) { throw; }  

You should already recognise some elements in the example: It starts with a multi line comment that begins and ends in line 1, there is something with main that may or may not be a correct main function, a try and a catch, an if and a lot of round things. Oh, and a link to the landing page of this series of posts.

As the comment on top asserts, the example is - to the best of my knowledge - correct C++. It does use C++14 features and should be forwards compatible to the upcoming changes in C++1z (which will become known as C++17 if the timetable of the standards committee holds true).

This is the first post of a series around that example and will focus on the outermost structure. For the sake of completeness, it should be noted that line 1 is exactly what it seems: A comment that holds no further meaning.

Trailing Return Types

Since C++11, it is possible to write a function T func(/* args */) using the trailing return type syntax, which is written as auto func(/* args */) -> T. This matches line 2 of the example fairly closely! In fact, since we now know that line two creates a function called main at file level, we also know what its return type must be for the program to be well formed: int. This is because only two versions of a global function with the name main are allowed by the C++ standard: int main() and int main(int, char**). You can also already see that trailing return types do not introduce a different kind of function, but are just a different way of writing the same thing.

decltype

Well. We know now that the thing following the arrow must somehow resolve to the type int. It is also reasonable to expect that the try is not part of the type anymore.

In a way, decltype is one of the simplest parts of C++, as its semantics are really easy to understand: It returns the type instead of the value of its expression. This means that the return type of the main function of the example is the type of 'O.o'.

And, as it turns out, 'O.o' is not just a "normal" character literal, but a multicharacter literal. Multicharacter literals are just what their name says: A character literal with multiple characters. As their content cannot be expected to find inside a single char, their type is int, which is exactly what we were expecting. Note that their actual value is not defined by the C++ standard.

Unlike decltype and trailing return types, which have been introduced with C++11, multicharacter literals have been around since the very first C++ standard in 1998.

What are trailing return types useful for?

Stepping away from the example for a moment, a fairly obvious question begs to be asked: "What are trailing return types useful for?" After all, they seem to say the exact same thing as the old syntax with more text.

The reason for their introduction is that they can help writing templates that could not otherwise be stated. Consider for example a simple function template that returns the maximum of two numbers. This is fairly easy if the two numbers are restricted to having the exact same type:

template<typename T>  
T max(T x, T y)  
{ return x > y ? x : y; }

As you may notice, this restricts the maximum function to a subset of what its implementation supports: The ternary operator (a?b:c) has some fairly fancy rules to find a common type for its two possible results. It would be nice if we could somehow leverage those rules to implement a more general version of this function.

Lets start with a naive (and wrong!) version, where we simply use decltype on the expression whose return type we want to reuse:

template<typename T, typename U>  
decltype(x > y ? x : y) max(T x, U y)  
{ return x > y ? x : y; }

Obviously, this cannot work, because x and y do not exist at the point of the decltype. However, we can now call trailing return types to the rescue to move the decltype to a point in the program where x and y do exist:

template<typename T, typename U>  
auto max(T x, U y) -> decltype(x > y ? x : y)  
{ return x > y ? x : y; }

Of course, the maximum function alone is not worth the effort to introduce this new syntax, but there are many additional cases where it can be useful to reuse the parameters in the return type. You can see this template in action on ideone.

What are multicharacter literals useful for?

Multicharacter literals are one of the really exotic features of C++. While they have been in the standard for a long time, their use is really hard to grasp at first glance:

  • Their value is undefined by the standard.
  • Their type is int.
  • They look like strings, but are really no strings.

Their purpose only becomes obvious when one looks at how they are used by C++ compilers: As long as they use at most as many characters as there are bytes in an int, the resulting int simply contains these characters one after another. This way, when looking at a memory dump, one can not only immediately spot them, but also read them without looking up values in a table.

Consider for example the following enumeration:

enum what {  
    STOP = 'STOP',
      GO = '  GO',
    WAIT = 'WAIT
};

When using this enumeration, the values are human-readable in a memory dump! If you want to see it in action have a peek at this example.

Gimme more!

This post is part 1 of 7 of a series on rarely used C++ features. To find the other parts of the series around this example of how to not write C++, see this overview page.

Directly continue to the next part, Part 2.

Daniel Schemmel

is currently employed at the Chair of Communication and Distributed Systems at RWTH Aachen University, where he researchs the testability of distributed systems. He can be reached at blog(at)gha.st.

Aachen, Germany, Terra, Sol, Milky Way, Laniakea SC https://gha.st/about/