Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sum function with return type large enough to hold result

It's a question from C++ Primer Chapter 16.2.3 (question 16.41):

Write a version of sum with a return type that is guaranteed to be large enough to hold the result of the addition.

I'm sure there might be some rather obscure STL function that can get this job done, but in the context of the chapter it introduces Standard Type Transformation Templates such as remove_reference<T> and make_signed<T> which I'm sure it intends for me to use to accomplish this, in conjunction with trailing return types. The best I can do is:

template <typename It> auto sum(It first, It second) -> typename make_unsigned<It>::type {
    return first+second;
}

This almost answers the question but not quite, it doesn't account for that fact that I could pass two unsigned ints which add to go outside the range of values that an unsigned int can hold (and so loops back down to zero). As far as I can tell the transformation templates can't help with that issue, is it somehow possible to deduce the return type as the next largest integral type up from the integral type deduced from the passed arguments?

like image 829
AntiElephant Avatar asked Nov 10 '15 21:11

AntiElephant


1 Answers

Since you want to do this at compile time, there is no way of knowing the value of the arguments the function will be invoked with. So you should protect agains overflowing at compile time, and the most obvious thing that comes to my mind is using a promotion trait class:

#include <iostream>
#include <limits>

template<typename T>
struct promote;

template<> // and so on for all types that you want to promote
struct promote<unsigned int> // type to be promoted from
{
    using type = unsigned long int; // type to be promoted to
};

// helper a la C++14
template<typename T>
using promote_t = typename promote<T>::type;

template <typename It> 
auto my_sum(It first, It second) -> promote_t<It>
{
    return static_cast<promote_t<It>>(first) + second; // promotion
}

int main()
{
    unsigned int a = std::numeric_limits<unsigned int>::max();
    unsigned int b = std::numeric_limits<unsigned int>::max();

    auto c = my_sum(a, b); // type is promoted to unsigned long int
    std::cout << "a = " << a << std::endl;
    std::cout << "b = " << b << std::endl;
    std::cout << "a + b = " << c << std::endl;
}

Live on Coliru

like image 170
vsoftco Avatar answered Nov 01 '22 07:11

vsoftco