In today’s post, I would like to go over the type trait std::common_type<>. This trait was introduced in C++11.
As per the specification, std::common_type<T1, T2, …Tn>::type refers to a type Tx in the given list, which the rest of the types in the list can be implicitly converted to. This works with built-in as well as user-defined types.
In the examples below, I will be using the Helper type:
template< class… T >
using common_type_t = typename common_type<T…>::type;
This helper was introduced in C++14.
Our first example demonstrates how this type trait works with built-in types:
Line 13 defines a variable var1, whose type will be the common type of the three types “int”, “float” and “double”. As you might have guessed correctly, the common type happens to be “double”. This is verified in Line 14, where we print the name of the type (please note that the exact name printed is compiler specific, but it should uniquely denote the type). The actual output is given in the following figure:
What about var2 in Line 16? Since a “char” can be implicitly converted to an “int”, but not vice versa, “int” happens to be the common type here.
We have a problem with var3 in Line 19. There is no implicit conversion between “const char *” and either “int” or “char”. Hence this is a compile-time error.
var4 on Line 22 will have the type “string” since “char *” can be implicitly converted to “string”.
var5 on Line 25 illustrates another trivial case since “void” is incompatible with any other type.
A slight variation of the above is var6. In this case, since pointer to any given type (except pointer to members) can be implicitly converted to “void *”, the type of var6 will be “void *” and the output confirms this.
Let us now consider some user-defined types. See the figure below.
There is nothing complex here. The “Root” class serves as the base class for all the other classes. Classes “A” and “B” derive virtually from “Root” and because of this, class “E” (Line 29) gets a single instance of “Root” via both “A” and “B”.
Here is the code fragment showing the usage of common_type<> in the context of this class hierarchy.
In Line 35, we define variables to hold the different user types.
Line 38 defines a convenient lambda function to print the name of the class corresponding to a given object.
Line 44: Since “C” is derived from “A”, “A” will be the common type here. Hence we can reference objects of type both “A” and “C” using this type.
The output from the sample program is here:
Line 48: “Root” is the virtual base class of “C” through “A”. Hence that is also the common type in this case.
Line 52: Since “B” is a direct base class of “E”, the common type here will be “B”.
Line 56: There should be no surprise here. “Root” is the common type.
Line 64 uses only one type, which is permitted. The common type is, trivially, that single type “D”.
Lines 69, 70 and 71 show an important property of common_type trait. Although the pairs <A, B>, <C, D> and <D, E> all share a common base class “Root”, that type is not identified as the common type through inference. The type trait only determines a common type, if possible, from among the listed types. This could come as a surprise to a few (it did to me initially!), but that is the reality.
Hope today’s discussion throws sufficient clarity on this type trait. You can download the example code from here.
Have a nice weeekend!
Recent Comments