Thursday, February 11, 2010

Episode Fifteen: What's In A Name?

In C++'s world, a rose by any other name may not smell as sweet. Names in C and C++ do not only identify things, but they can convey properties of such things. Consider the following declaration:
void rose( color_type color = color_type::red );
Here the name rose is an identifier, but it also states that -by default- roses are red. Function names cannot be held in variables, but function pointers (or references) can. But if we were to take such pointer (or reference), we would have to drop the associated knowledge that roses are red by default.
void (*rose_ptr)() = &rose; // Error, rose takes one argument
void (*rose_ptr)( color_type ) = &rose; // Ok, but the default color is lost
But that's not it. Function names carry other information as well. A function name may identify a set of functions, as in an overloaded function (an expression as `&function_name` is the only way to create an overloaded function pointer). Additionally, in presence of argument dependent lookup, an unqualified function name may identify a set of functions in different namespaces.

What's in a function name? In C++ the answer would be default parameters, overload resolution and argument dependent lookup. All those get dropped when taking a function pointer (or reference). This duality of names can cause portability problems; consider the following fragment from the standard (n3000),
17.6.4.4.3:
An implementation shall not declare a global or non-member function signature with additional default arguments.
17.6.4.5.2:
An implementation may declare additional non-virtual member function signatures within a class:
—by adding arguments with default values to a member function signature;
—by replacing a member function signature with default values by two or more member function signatures with equivalent behavior;
The fact that an implementation may declare additional arguments with default values to member functions means that the exact signature type of such standard functions is not standardized. As a result, it is impossible to portably create a pointer to a standard library member function. C++0X's `auto` and `decltype` are of little help, since the exact function type must be known to cast the function pointer to it in case there are overloads. On the other hand, global or non-member function exact signatures are standardized.

If in need to take a function pointer to a function whose exact type is unspecified, the solution is to wrap it around another function with the expected signature and take the pointer of such function. A generic solution can only be created by means of the preprocessor, the preprocessor is the only C++ feature that can operate on identifiers; even in the shape of things to come with C++0X.

Finally, is worth noticing that templates too have default arguments, and so there is also more in their names than just identifiers.

No comments:

Post a Comment