【发布时间】:2016-12-05 15:50:50
【问题描述】:
动机
在某种程度上,其动机是模仿 C++ 中的数学概念,主要是为了允许编写极其通用的算法。稍后会详细介绍动机。
首先,一些定义。
定义
- 如果
B可转换为A,则类型A包含另一种类型B,至少几乎不会丢失信息*。这类似于超集B ⊆ A的数学概念。 - 如果
C包含C X C,则C类型在operatorX下闭合。 -
operatorX下的A类型的闭包C是包含A并在operatorX本身下关闭的类型。C可能不是唯一的。C可能不存在。 -
A和B在operatorX和operatorY下的闭包C是包含A和B的类型,并且在operatorX和 @98765434 下关闭.这需要定义A X B、B Y A等。
*注意:这是一个相当不精确的说法,但如果没有很多繁琐的限制,很难给出严格的定义。例如:int 包含 char,double 包含 float,double 包含 int32_t,因为它具有 52 位精度,但 float 不包含 int32_t,因为它只有23 位精度。
问题
给定两种类型 T 和 U,都定义了 operator+ 和 operator*。如果找不到它们的闭包或发出错误,什么是有效的方法?
请注意,它们的运算符应该被认为是绝对疯狂的,也就是说,T 甚至可能不会在 operator+ 下关闭,Σ(k ∈ [0, n)) T 可能是依赖于 n 的类型。
例如,如果我们只想要operator+下的闭包
closure<unsigned, unsigned char>::type //unsigned
可以很容易地实现为
template<typename T, typename U> struct closure {
using type = decltype(std::declval<T>() + std::declval<U>());
};
但这并不适用于所有类型,因为如上所述,decltype(std::declval<T>() + std::declval<U>()) 可能不会关闭。
更多关于动机
假设某个通用算法需要处理多种不同类型。如果我们要创建一个变量来存储中间值,它的类型应该是它们的闭包。
最简单的示例是将int 添加到float,使用double 作为中间存储。但在这种情况下,语言实际上指定 float 是 int 和 float 的闭包,这在某些情况下是次优的。
现在由于算法是通用的,我们需要一些方法来找到闭包,而无需事先知道这些类型及其运算符是什么。
我可能有点得意忘形,实际上创造了一些愚蠢的东西。
标题注释
我不能称它为“找到类型的闭包”,闭包通常意味着编程中的其他东西 :P 。如果有人建议一个更好的名字,我很乐意改变它。
【问题讨论】:
-
你想如何处理上溢/下溢?
int在技术上甚至不会在operator+(int,int)下关闭,除非您明确地将加法视为对整数大小取模。 -
@mindriot 有符号整数溢出是未定义的行为 ;)
-
@mindriot 这也是其背后动机的一部分。我认为
operator+下的int的理想关闭实际上类似于long long或大于int的任何东西 -
是的,这使得它非常困难。分析所有值都是
int32_ts 的(a+b)+c怎么样?第一个操作的闭包产生int64_t,因此如果没有关于可能范围的更多信息,第二个闭包将不得不产生int128_t,即使我们知道这不是必需的。所以,没有传递性。 -
@mindriot 实际上,如果我们在编译时知道只有三个添加,那么它们的闭包实际上就是
int64_t。这当然永远无法解决运行时问题,甚至可能无法解决一些编译时问题。这是防止事故发生的另一层检查。此外,这不仅仅是溢出错误。