Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

Click here to view the latest version of this page.
PrevUpHomeNext

common_type

#include <boost/type_traits/common_type.hpp>

namespace boost {
  template <class ...T>  struct common_type;
}

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 <class T>
struct common_type<T> {
    typedef T type;
};

template <class T, class U>
struct common_type<T, U> {
    typedef decltype(declval<bool>() ? declval<T>() : declval<U>()) 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_VARIADIC_TEMPLATES is defined) then the maximum number of template arguments is 3.

Configuration macros

When the compiler does not support static assertions then the user can select the way static assertions are reported. Define

The default behavior is to use mpl assertions in this case, but setting BOOST_COMMON_TYPE_USES_STATIC_ASSERT may reduce compile times and header dependencies somewhat.

Depending on the static assertion used you will have an hint of the failing assertion either through the symbol or through the text.

When possible common_type is implemented using decltype. Otherwise when BOOST_COMMON_TYPE_DONT_USE_TYPEOF is not defined it uses Boost.TypeOf.

Tutorial

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.

How to get the common type of types with explicit conversions?

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;
};
How important is the order of the 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>.

Can the 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>.

How common_type behaves 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.

Can you explain the pros/cons of 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 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.


PrevUpHomeNext