JOIN
Get Time
features   
Five things you didn't know about C++, Part 3

Author
By bmerry
TopCoder Member

Introduction

The previous two articles in this series discussed a number of features of C++ which are not well known, but are nevertheless useful for some tasks. By popular demand, I'm concluding with a list of another five features, this time featuring ones that you're almost certain never to use in your code. Amaze your friends, confuse your teachers, enter the IOCCC and ask yourself how on Earth these features ended up in the standard.

Returning void

I'll start with the one feature on this list that is used in practice, although very rarely. In a void function, the return statement can usually only be used without an argument. However, it is legal to return an expression of void type, for example

return (void) (a + b);

This seems quite useless, since any expression could just as easily be written on a previous line (or eliminated, provided it has no side effects), and in fact the above statement is illegal in C.

The reason that this syntax was introduced in C++ is to facilitate template-based function wrappers, which are used by the STL. In the first article, I referred to mem_fun, which wraps a pointer to member function as a function object. An abbreviated version of this might look like this:

template<typename Result, typename X>
struct wrapper
{
    Result (X::*ptmf)();
    Result operator()(X *x) { return (x->*ptmf)(); }
};

Without this feature, it would be necessary to specialize this class for a void return type.

Trigraphs

See if you can guess what the following program will produce, if compiled with a C++98 conforming compiler (you can use GCC with the -std=c++98 switch).

#include <iostream>
int main()
{
    std::cout << "`What the heck??' he cried.\n";
}

In fact, it prints out

`What the heck^ he cried.

The reason is that C and C++ specify 9 trigraphs: sequences of 3 characters (the first two being question marks) that are substituted by certain other characters in the first phase of lexical analysis. The replacement characters all appear on any keyboard, so this is generally considered to be one of the most useless features of C. In fact, GCC doesn't even process them by default, which is why you have to give it the -std flag.

auto variables

The award for the most useless keyword goes to auto. If you've read about the upcoming version of C++, you'll know that it is being assigned a new meaning, which makes it possible to write code like

for (auto i = container.begin(); i !=  container.end(); i++)

and have the compiler figure out the correct type for i. However, the keyword already exists in C++ today (the language designers like to recycle keywords in order to minimize the chance of a new keyword conflicting with identifiers in old code). It can be applied to a local variable to indicate that it has automatic storage duration — which is the default, so it has no actual effect.

The C++ standard suggests that it may be used to disambiguate statements that could be either expressions or declarations (for example, A (*a)() is either a declaration of a function pointer a, or a typecast of *a to type A followed by a function call on the resulting object). However, the standard also requires that such ambiguous statements are always treated as declarations, so once again the keyword has no effect.

Conversion constructors and explicit typecasts

In C++, one can define ways to convert objects to new classes that you define, simply by writing a constructor that accepts an object of the old type. Of course, not every single-argument constructor is intended for this purpose, so the explicit keyword is available to indicate constructors that should not be used for implicit conversions. An example is the vector<T> constructor that takes an integer argument: it certainly doesn't make sense to allow an integer to be passed to a function that expects a vector and have an automatic conversion take place, and if you try it you will get an error.

However, a quirk in the standard means that explicit typecasts will still be handled with such constructors. In other words, the expression

(vector<int>) 3

is legal and will construct a temporary vector<int> with 3 elements.

Array subscripting order

I believe this one is fairly quite well known to old-school C programmers, but it carries over to C++. Unless the operator is overloaded, the expression a[b] is defined to be equivalent to *(a + b), and one of a and b must be integral and the other must be a pointer. Look at that definition again and look for an asymmetry: there is none! That means that 2[a] and a[2] are exactly the same, although one would have to question the coding style of anyone who used the former.

Operator overloading actually makes this (in theory) fractionally more useful in C++ than in C. If a has an overloaded operator[] as well as an implicit conversion to a pointer type, then a[2] will use operator[] while 2[a] will first convert a to a pointer before doing normal subscripting. I very much doubt that this is useful in practice, though.