我有理解以下段落Per C ++ 11 Standard N3485 Section 14.1.7。 我认为理解基本原理而不是记住事实更为重要。
非类型模板参数不应声明为具有浮点,类或void类型 [例如:
template<double d> class X; // error template<double* pd> class Y; // OK template<double& rd> class Z; // OK
-end example]
我对此规则有一些疑问:
为什么floating point
类型不能用作模板参数?这背后的基本原理是什么?我知道在C ++ 11之前这是正确的,对于C ++ 11标准来说也是如此。
为什么可以将pointer
或reference
用作浮动点类型作为非模板参数,而不是原始浮点类型?什么是这里有很大的不同?
感谢您的帮助。
答案 0 :(得分:8)
为什么浮点类型不能用作模板参数?这背后的理由是什么?
虽然我无法给出最终的理由,但我可以想象,专门用于接受浮动指针值作为参数的模板会出现问题。
浮点数之间的等式比较是棘手的(在某种意义上它有时会产生意外结果),并且在匹配特化时,编译器必须在提供的参数和模板所在的值之间执行相等性检查。专门。
另一个类似的问题是确定相同类模板的两个实例是否实际上是同一类型:
template<double D>
struct X
{
// ...
};
int main()
{
X<3.0> x;
X<some_constant_expression()> y;
}
同一类的x
和y
个实例?要做出决定,必须在3.0
和some_constant_expression()
之间执行相等检查。
为什么可以将浮点类型的指针或引用用作非模板参数,而不是原始浮点类型?
关于上面的场景,关于指针的答案很简单:指针是整数值,指针之间的比较很明确。
关于参考文献,证据显示它们实际上被视为指针:
#include <type_traits>
double a = 0.0;
double b = 0.0;
template<double& D>
struct X : std::false_type { };
template<>
struct X<a> : std::true_type { }
int main()
{
static_assert(X<a>::value, "!"); // Does not fire
static_assert(X<b>::value, "!"); // Fires
}
另外,根据C ++ 11标准的第14.4 / 1段:
如果
,则两个 template-ids 引用相同的类或函数- [...]
- 它们对应的整数或枚举类型的非类型模板参数具有相同的值和
- [...]
- 引用类型的相应非类型模板参数引用相同的外部对象 或功能和
- [...]
[示例:
template<class E, int size> class buffer { / ... / }; buffer<char,2*512> x; buffer<char,1024> y;
声明
x
和y
属于同一类型,并且[...]
以上显示,确定同一个类模板的两个不同实例是否实际上是同一个类需要在用作模板参数的常量表达式之间进行相等性检查。
答案 1 :(得分:2)
要弄清楚这一点,请考虑整数类型和指针始终与其文字表示形成一对一的关系。
但接下来让我们考虑一个非类型模板Foo<float>
。
假设它专门用于非二进制可表示的数字,例如0.1
:Foo<0.1>
,让我们说编译器根据特化来装饰符号名称,最后你会得到像{{1}这样的东西。因为编译器使用“向下舍入”用于IEEE 754 32位代表。
但是,让我们说这个代码的用户编译是这样一种方式,编译器选择“round to positive infinity”。然后专业化的名称是Foo_3DCCCCCC
,这是一个完全不同于以前专门用于另一个翻译单元的功能。
除非你开始制定各种各样的规则来处理所有这些事情(那么NaN,无穷大和其他非正常数字呢?)然后你会让自己陷入不匹配和各种可能的问题