给定一个类型,如何派生出一个通用的更广泛的类型(例如,用于溢出安全求和)?

Given a type, how to derive a generic wider type (e.g. for overflow-safe summation)?

举个例子,假设我有一个函数计算给定 std::vector<T>:

的平均值
    template<class T>
    T vec_average(const std::vector<T>& vec) {
      assert(!vec.empty());
      T accumulator = vec[0] - vec[0]; // Init to 0.
      for (const auto& el : vec) { accumulator += el; }
      return accumulator / double(vec.size());
    }

现在当然要求所有运算符(+=、/、-)都为 T 定义并且可能存在舍入问题,但更严重的问题是调用 vec_average 与 "small type" 类似 uint8_t 将很快导致由于溢出导致的不良结果。

type_traits 救援!你可能会说,这确实是我解决这个问题的初步尝试:

    template<class T>
    struct safe_accum {
      typedef typename std::conditional<std::is_integral<T>::value, int64_t, T>::type type;
    };

    template<class T>
    T vec_average(const std::vector<T>& vec) {
      assert(!vec.empty());
      safe_accum<T>::type accumulator = vec[0] - vec[0]; // Init to 0.
      for (const auto& el : vec) { accumulator += el; }
      return accumulator / double(vec.size());
    }

但是:这只解决了所有整数和浮点类型。一旦我有了结构或 class 类型,它就会崩溃(因为 std::is_integral 不适用于 class 类型):

    struct Vector3uc {
      uint8_t data[3];
      // Operator definitions etc...
    };

    void foobar() {
      std::vector<Vector3uc> bla;
      // ...
      Vector3uc avg = vec_average(bla); // Won't work!
    }

因此,我的问题是:我能否拥有适用于 "classy" 输入类型 Tsafe_accum 类型?

例如,我想告诉编译器 safe_accum<Vector3uc>::type 必须生成 Vector3ll(int64_t 的 3 向量),而 safe_accum<Vector3f>::type 只是 Vector3f.

我很确定它可以通过 typedefs and/or 模板专业化来完成,但在我的 C++ 知识中这是一个灰色地带...

PS: 需要说明的是,我不介意在其中手动定义这些翻译 (Vector3uc --> Vector3ll)每个相关类型的代码,只要每个类型只有一两行。

你可以给你的特质更多的自定义点:

template <class T, typename Enabler = void>
struct safe_accum
{
    using type = T;
};

template <class T>
struct safe_accum<T, std::enable_if_t<std::is_integral<T>::value>>
{
    using type = int64_t;
};

class Vector3uc;
class Vector3ll;

template <>
struct safe_accum<Vector3uc>
{
    using type = Vector3ll;
};
// ...

您可以引入一个类型特征,safe_accum 将使用它来获取中间累加器类型。

template< typename T >
struct accumulator_type;

template< >
struct accumulator_type< float > { typedef float type; };

template< >
struct accumulator_type< double > { typedef double type; };

template< >
struct accumulator_type< char > { typedef int type; };

template< >
struct accumulator_type< unsigned char > { typedef unsigned int type; };

// etc. - you have to specialize it for every type you want to accumulate

template<class T>
T vec_average(const std::vector<T>& vec) {
    assert(!vec.empty());
    typename accumulator_type<T>::type accumulator{}; // Init to 0.
    for (const auto& el : vec) { accumulator += el; }
    return accumulator / double(vec.size());
}

您可以使用 Boost.Integer 等工具在 accumulator_type 实现中为累加器自动 select 更大的整数类型。