...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
Header: #include
<boost/type_traits/common_type.hpp>
or #include <boost/type_traits.hpp>
namespace boost { template <class... T> struct common_type; template<class... T> using common_type_t = typename common_type<T...>::type; // C++11 and above }
common_type
is a traits class
used to deduce a type common to a several types, useful as the return type
of functions operating on multiple input types such as in mixed-mode arithmetic..
The nested typedef ::type
could be defined as follows:
template <class... T> struct common_type; template <class T, class U, class... V> struct common_type<T, U, V...> { typedef typename common_type<typename common_type<T, U>::type, V...>::type type; }; template <> struct common_type<> { }; template <class T> struct common_type<T> { typedef typename decay<T>::type type; }; template <class T, class U> struct common_type<T, U> { typedef typename decay< decltype( declval<bool>()? declval<typename decay<T>::type>(): declval<typename decay<U>::type>() ) >::type type; };
All parameter types must be complete. This trait is permitted to be specialized
by a user if at least one template parameter is a user-defined type. Note: Such specializations are required when only
explicit conversions are desired among the common_type
arguments.
Note that when the compiler does not support variadic templates (and the
macro BOOST_NO_CXX11_VARIADIC_TEMPLATES
is defined) then the maximum number of template arguments is 9.
In a nutshell, common_type
is a trait that takes 1 or more types, and returns a type which all of the
types will convert to. The default definition demands this conversion be
implicit. However the trait can be specialized for user-defined types which
want to limit their inter-type conversions to explicit, and yet still want
to interoperate with the common_type
facility.
Example:
template <class T, class U> complex<typename common_type<T, U>::type> operator+(complex<T>, complex<U>);
In the above example, "mixed-mode" complex arithmetic is allowed.
The return type is described by common_type
.
For example the resulting type of adding a complex<float>
and complex<double>
might be a complex<double>
.
Here is how someone might produce a variadic comparison function:
template <class ...T> typename common_type<T...>::type min(T... t);
This is a very useful and broadly applicable utility.
Another choice for the author of the preceding operator could be
template <class T, class U> typename common_type<complex<T>, complex<U> >::type operator+(complex<T>, complex<U>);
As the default definition of common_type
demands the conversion be implicit, we need to specialize the trait for complex
types as follows.
template <class T, class U> struct common_type<complex<T>, complex<U> > { typedef complex< common_type<T, U> > type; };
common_type<>
template arguments?
The order of the template parameters is important.
common_type<A,B,C>::type
is not equivalent to common_type<C,A,B>::type
, but to common_type<common_type<A,B>::type, C>::type
.
Consider
struct A {}; struct B {}; struct C { C() {} C(A const&) {} C(B const&) {} C& operator=(C const&) { return *this; } };
The following doesn't compile
typedef boost::common_type<A, B, C>::type ABC; // Does not compile
while
typedef boost::common_type<C, A, B>::type ABC;
compiles.
Thus, as common_type<A,B>::type
is undefined, common_type<A,B,C>::type
is also undefined.
It is intended that clients who wish for common_type<A,
B>
to be well defined to define it themselves:
namespace boost { template <> struct common_type<A, B> {typedef C type;}; }
Now this client can ask for common_type<A,
B, C>
(and
get the same answer).
Clients wanting to ask common_type<A,
B, C>
in
any order and get the same result need to add in addition:
namespace boost { template <> struct common_type<B, A> : public common_type<A, B> {}; }
This is needed as the specialization of common_type<A,
B>
is not be used implicitly for common_type<B,
A>
.
common_type
of two types
be a third type?
Given the preceding example, one might expect common_type<A,B>::type
to be C
without any intervention from the user. But the default common_type<>
implementation doesn't grant that.
It is intended that clients who wish for common_type<A,
B>
to be well defined to define it themselves:
namespace boost { template <> struct common_type<A, B> {typedef C type;}; template <> struct common_type<B, A> : public common_type<A, B> {}; }
Now this client can ask for common_type<A,
B>
.
common_type
behave with
pointers?
Consider
struct C { }: struct B : C { }; struct A : C { };
Shouldn't common_type<A*,B*>::type
be C*
?
I would say yes, but the default implementation will make it ill-formed.
The library could add a specialization for pointers, as
namespace boost { template <typename A, typename B> struct common_type<A*, B*> { typedef common_type<A, B>* type; }; }
But in the absence of a motivating use cases, we prefer not to add more than the standard specifies.
Of course the user can always make this specialization.
common_type
against Boost.Typeof?
Even if they appear to be close, common_type
and typeof
have different
purposes. You use typeof
to get the type of an expression, while you use common_type
to set explicitly the type returned of a template function. Both are complementary,
and indeed common_type
is
approximately equivalent to decltype(declval<bool>()
? declval<T>()
: declval<U>())
.
common_type
is also similar
to promote_args<class ...T>
in
boost/math/tools/promotion.hpp
, though
it is not exactly the same as promote_args
either. common_type<T1, T2>::type
simply represents the result of some operation on T1
and T2
, and defaults to the
type obtained by putting T1
and T2
into a conditional
statement.
It is meant to be customizable (via specialization) if this default is not appropriate.