假设我有一个4值向量联合,用于保存空间坐标或颜色,我希望使用两个函数之一来转换整数和实数格式,我将如何构造函数?
我的(失败)尝试是:
template<class T,
class S,
typename = std::enable_if_t<std::is_floating_point<T>>,
typename = std::enable_if_t<std::is_integral<S>>>
Math::Vec4<T> Colour(S r, S g, S b, S a)
{
return Vec4<T>(r / static_cast<T>(255),
g / static_cast<T>(255),
b / static_cast<T>(255),
a / static_cast<T>(255));
};
template<class T,
class S,
typename = std::enable_if_t<std::is_integral<T>>,
typename = std::enable_if_t<std::is_floating_point<S>>>
Math::Vec4<T> Colour(S r, S g, S b, S a)
{
return Vec4<T>(static_cast<T>(r * 255),
static_cast<T>(g * 255),
static_cast<T>(b * 255),
static_cast<T>(a * 255));
};
这里的目的是在T为实数且S为整数(从整数转换为实数)的情况下实例化1,对于T为整数且S为实的情况为2。理想情况下,我想为两者使用相同的名称,并且编译器根据输入类型决定使用哪个。目前我收到编译错误,&#34;功能模板已经定义了#34;。
另外,这是个坏主意吗?
更新:我根据格子呢的答案制作了一个最小的复制品,它没有编译(无法推断模板参数)。我在哪里错了?
#include <limits>
#include <type_traits>
template<class T>
union Vec3
{
typedef T value_type;
struct
{
T r, g, b;
};
Vec3() = default;
Vec3(T R, T G, T B) : r(R), g(G), b(B) {}
};
template<class T,
class S,
std::enable_if_t<std::is_floating_point<T>::value && std::is_integral<S>::value> * = nullptr>
Vec3<T> Colour(Vec3<S> const & rgb)
{
return Vec3<T>(static_cast<T>(rgb.r) / static_cast<T>(std::numeric_limits<S::value_type>::max()),
static_cast<T>(rgb.g) / static_cast<T>(std::numeric_limits<S::value_type>::max()),
static_cast<T>(rgb.b) / static_cast<T>(std::numeric_limits<S::value_type>::max()));
}
template<class T,
class S,
std::enable_if_t<std::is_integral<T>::value && std::is_floating_point<S>::value> * = nullptr>
Vec3<T> Colour(Vec3<S> const & rgb)
{
return Vec3<T>(static_cast<T>(rgb.r * static_cast<S::value_type>(std::numeric_limits<T>::max())),
static_cast<T>(rgb.g * static_cast<S::value_type>(std::numeric_limits<T>::max())),
static_cast<T>(rgb.b * static_cast<S::value_type>(std::numeric_limits<T>::max())));
}
int main(void)
{
Vec3<float> a(1.0f, 0.5f, 0.25f);
Vec3<char> b;
b = Colour(a);
}
答案 0 :(得分:4)
您的问题是默认模板参数的差异不会声明不同的模板。这就是你得到重新定义错误的原因。
另一个问题是,您需要获取::value
和std::is_integral
特征的std::is_floating_point
,因为std::enable_if_t
需要bool
,而不是std::integral_constant
{1}}。
要解决此问题,您可以使用标准的SFINAE技巧来声明类型的模板参数,该参数取决于enable_if_t
结果:
template<class T,
class S,
std::enable_if_t<
std::is_floating_point<T>::value && std::is_integral<S>::value
>* = nullptr>
Math::Vec4<T> Colour(S r, S g, S b, S a)
{
return Vec4<T>(r / static_cast<T>(255),
g / static_cast<T>(255),
b / static_cast<T>(255),
a / static_cast<T>(255));
};
template<class T,
class S,
std::enable_if_t<
std::is_integral<T>::value && std::is_floating_point<S>::value
>* = nullptr>
Math::Vec4<T> Colour(S r, S g, S b, S a)
{
return Vec4<T>(static_cast<T>(r * 255),
static_cast<T>(g * 255),
static_cast<T>(b * 255),
static_cast<T>(a * 255));
};
答案 1 :(得分:4)
@TartanLlama的答案提出了技术方法。在这里,我对你更基本的问题发表看法
另外,这是个坏主意吗?
是的,我想是的。
对我而言,Coulour
的两个重载似乎彼此相反。因此,你应该表达并给他们正确的名字。
没有人会期望Colour(Colour(r,g,b,s))
成为身份操作(这样的功能在这里不存在,但解释清楚)。
colour_to_spatial
/ spatial_to_colour
或colour
/ coulor_invert
之类的内容会更合适。
接下来,可能存在技术问题。从Integer
到Floating-point
的转换回Integer
通常不具有确定性,因为可能会出现舍入错误,从而导致不同的结果。所以我建议在这里小心并使用适当的舍入例程,或者更好的是,使用固定宽度的十进制类型。