【发布时间】:2016-06-08 20:52:15
【问题描述】:
在我正在进行的一个项目中,我有一个可以存储各种类型数字的数据结构,并且需要保证不会意外丢失精度/信息。
因为标准 C++ 允许隐式发生一些有损转换,所以我创建了一个类型特征,用于确定我将允许哪些转换并禁止我不喜欢使用 SFINAE 的转换。
我意识到我是如何做到这一点的存在一个微妙的问题。
这是一段代码摘录:
// If it is between two floating point types, no truncation is allowed.
template <typename A, typename B>
struct safe_numeric<A,
B,
typename std::enable_if<(std::is_floating_point<A>::value &&
std::is_floating_point<B>::value)>::type> {
static constexpr bool value = sizeof(A) >= sizeof(B);
};
本意是,float可以提升为double或long double,double可以提升为long double,但不能变小。
但是,在这里使用sizeof 是不好的,因为在sizeof(double) == sizeof(float) 所在的机器上,根据此模板将double 转换为float 是合法的。这将使我的程序不可移植——开发人员可以编写在他们的机器上编译和运行良好的代码,事实上,不会丢失精度。但是在其他一些机器上,它可能只是无法编译并抱怨不安全的转换。
例如,我真正想要的是获得[4.13] [conv.rank] 中标准所述的conversion rank。我想对浮点和整数类型都这样做。
例如,我可以自己滚动:
template <typename T>
struct conversion_rank;
template <>
struct conversion_rank<float> {
static constexpr std::size_t value = 0;
};
template <>
struct conversion_rank<double> {
static constexpr std::size_t value = 1;
};
template <>
struct conversion_rank<long double> {
static constexpr std::size_t value = 2;
};
并使用该元函数而不是sizeof。
如果我想对整数类型执行此操作...还有很多。
还有一个问题是整数类型不需要存在,所以我可能想尝试检测这种情况,以便 conversion_rank 模板能够编译。
有没有更好/标准的方法来解决这个问题?我在标准库中没有找到“conversion_rank”元函数,我猜该标准实际上并没有给出数值,它只是指定了“更高”和“更低”。但也许boost/其他一些不那么费力且不依赖sizeof的策略?
【问题讨论】:
-
既然你本质上想要阻止变窄,只需 SFINAE 对
Dest{std::declval<Source>()}的良构性? -
这正是我希望得到的答案,谢谢!
-
其实我收回了。如果你想要完全的可移植性,你必须用等级来写这个。
{}将允许long long到long在 LP64 上。 -
@T.C.
long long到long的转换是标准允许的,还是编译器中的错误? -
我很确定给定两个整数类型
T1和T2具有相同的大小但转换排名低于int,无法确定哪个具有更大的排名使用通常的 TMP 技术,无需对其进行硬编码。因此,如果您想要 100% 的可移植性,您要么必须自己进行硬编码,要么找人为您完成 :)