具有足够大的返回类型以保存结果的 Sum 函数

2022-01-09 00:00:00 sum templates c++ c++11

这是来自 C++ Primer Chapter 16.2.3 的一个问题(问题 16.41):

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

编写一个返回类型为的 sum 版本保证足够大以容纳加法的结果.

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

我确信可能有一些相当晦涩的 STL 函数可以完成这项工作,但在本章的上下文中,它介绍了标准类型转换模板,例如 remove_reference<T>make_signed<T> 我确信它打算让我用来完成此任务,并与尾随返回类型一起使用.我能做的最好的就是:

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;
}

这几乎回答了这个问题,但并不完全,它不能解释我可以传递两个 unsigned int 的事实,它们相加超出了 unsigned 的值范围int 可以保持(因此循环回零).据我所知,转换模板无法解决这个问题,是否有可能从传递的参数推导出的整数类型中推断出返回类型为下一个最大的整数类型?

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?

推荐答案

由于您想在编译时执行此操作,因此无法知道将调用函数的参数的值.所以你应该在编译时防止溢出,我想到的最明显的事情就是使用提升特征类:

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;
}

在 Coliru 上直播

相关文章