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

PrevUpHomeNext

common_type

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.

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 does 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.

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


PrevUpHomeNext