基于类型

时间:2015-06-17 08:28:25

标签: c++ templates

假设我有一个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);
}

2 个答案:

答案 0 :(得分:4)

您的问题是默认模板参数的差异不会声明不同的模板。这就是你得到重新定义错误的原因。

另一个问题是,您需要获取::valuestd::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_colourcolour / coulor_invert之类的内容会更合适。

接下来,可能存在技术问题。从IntegerFloating-point的转换回Integer通常不具有确定性,因为可能会出现舍入错误,从而导致不同的结果。所以我建议在这里小心并使用适当的舍入例程,或者更好的是,使用固定宽度的十进制类型。