基于类型的条件编译
Conditional compile based on type
假设我有一个 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_integral
和std::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_colour
或 colour
/coulor_invert
这样的东西会更合适 imo。
接下来,可能存在技术问题。从Integer
到Floating-point
再转换回Integer
通常不是确定性的,因为可能会发生舍入误差,从而导致不同的结果。所以我建议在这里要小心,使用适当的舍入例程,或者更好的是,固定宽度的十进制类型。
- 如何摆脱为条件编译定义预处理器宏的需要?
- 使用 constexpr 替换 #define 和 #ifdef 进行条件编译
- 基于宏中传递的字符串的条件编译
- Eclipse CDT 条件编译?
- 除了使用 #define 进行条件编译之外,还有其他选择吗?
- 为什么有条件编译运算符模板会更改另一个运算符的可用性?
- C 模板:如何根据数据类型有条件编译不同的代码
- 多个条件编译符号
- 如何在不弄乱库 API 的情况下实现条件编译?
- #ifdef SWIG:什么时候考虑这种条件编译
- 预处理器和模板参数或代码段的条件编译
- 条件编译和非类型模板参数
- 模板的条件编译
- 我们有没有像c一样用java进行条件编译
- 模板模板条件编译
- 基于模板值的条件编译
- 类的条件编译
- 系统头文件中的条件编译
- 与 32 位和 64 位应用程序相同的源代码的条件编译
- 基于windows版本的c++条件编译