Search code examples
c++templatesc++11metaprogramming

C++11 way to write template for picking bigger integer type?


At compile time in C++11 in a template function that takes 2 template parameters, both of which must be unsigned integer types, I'd like to have a local variable have the type of whichever of the two template parameters has more bits. In C++03 I might write something like:

template<bool, class T, class U>
struct pick_first;

template<class T, class U>
struct pick_first<true, T, U> {
    typedef T type;
};

template<class T, class U>
struct pick_first<false, T, U> {
    typedef U type;
};

template<class T, class U>
struct pick_bigger {
    typedef typename pick_first<(sizeof(T) >= sizeof(U)), T, U>::type type;
};

// usage
template<class uintX_t, class uintY_t>
void foo() {
    typename pick_bigger<uintX_t, uintY_t>::type mylocal = 0;
    // insert doing stuff with mylocal here
}

Can I leverage any of the new C++11 features to make this simpler? I know I could use variadic templates to make it work with more than just pairs of types, and instead of using pick_first I could write lots of specializations to make it work with the new int_leastX_t and int_fastX_t types. But I'm curious if there's just a plain better approach to this. Maybe somehow leveraging auto/constexpr/decltype?


Solution

  • Your pick_first is just std::conditional in C++11, so you could write

    template<class T, class U>
    struct wider {
        using type = typename std::conditional<sizeof(T) >= sizeof(U), T, U>::type; // I'm using the C++11 type alias feature 1) to educate people about them and 2) because I like them better than typedefs.
    };
    

    If you just want a type suitable for holding the result of some expression involving both types and don't necessarily need exactly one of the two types then std::common_type, or perhaps auto, is the best solution:

    template<class uintX_t, class uintY_t>
    void foo() {
        typename std::common_type<uintX_t, uintY_t>::type mylocal = 0;
        // insert doing stuff with mylocal here
    }
    
    // or
    template<class uintX_t, class uintY_t>
    void foo(uintX_t x, uintY_t y) {
        auto mylocal = x + y;
    }
    

    and your implementation of pick_bigger is missing a typename in there: typedef typename pick_first<(sizeof(T) >= sizeof(U)), T, U>::type type;