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

问题描述 投票:3回答:2

仅作为示例,假设我有一个计算给定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_averageuint8_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());
    }

但是:仅解决所有整数和浮点类型的问题。一旦有了结构或类类型,它就会崩溃(因为std::is_integral在类类型上不起作用):

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

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

因此,我的问题是:我是否也可以使用适用于“经典”输入类型safe_accumT类型?

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

我很确定可以使用typedef和/或模板专门化来完成,但这在我对C ++的了解中只是一个灰色区域...

PS:为清楚起见,我不介意在代码中为每种相关类型手动定义这些翻译(Vector3uc-> Vector3ll),只要每个翻译类型仅一行或两行类型。

c++ templates typetraits integer-overflow
2个回答
3
投票

您可能会给您的特征更多的定制点:

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

0
投票

您可以引入类型特征,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实现中的累加器自动选择较大的整数类型。

© www.soinside.com 2019 - 2024. All rights reserved.