基于类型的条件编译

Conditional compile based on type

本文关键字:条件 编译 类型 于类型      更新时间:2023-10-16

假设我有一个 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。 理想情况下,我想对两者使用相同的名称,并让编译器根据输入类型决定使用哪个名称。 目前我收到编译器错误,"函数模板已定义"。

另外,这是一个坏主意吗?

更新:我根据 Tartan 的答案制作了一个最小的重现,它不会编译(无法推断模板参数)。 我哪里错了?

#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);
}

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

另一个问题是,您需要::value std::is_integralstd::is_floating_point特征,因为std::enable_if_t期望bool,而不是std::integral_constant

要解决此问题,您可以使用标准的 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));
};

@TartanLlama的答案提供了技术方法。在这里,我对你更基本的问题发表我的看法

另外,这是一个坏主意吗?

是的

,我想是的。

对我来说,似乎你的两个超载Coulour是相反的。因此,您应该表达这一点并赋予它们适当的名称

没有人会期望Colour(Colour(r,g,b,s))是标识操作(这里不存在这样的函数,但解释很清楚)。

colour_to_spatial/spatial_to_colourcolour/coulor_invert 这样的东西会更合适 imo。

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