仅作为示例,假设我有一个计算给定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
像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());
}
但是:仅解决所有整数和浮点类型的问题。一旦有了结构或类类型,它就会崩溃(因为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_accum
的T
类型?
例如,我想告诉编译器,safe_accum<Vector3uc>::type
必须产生一个Vector3ll
(int64_t的3个向量),而safe_accum<Vector3f>::type
只是一个Vector3f
。
我很确定可以使用typedef和/或模板专门化来完成,但这在我对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
实现中的累加器自动选择较大的整数类型。