理解非类型模板参数

understanding of non-type template parameters

本文关键字:参数 类型      更新时间:2023-10-16

我理解以下段落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]

我对这个规则有一些疑问:

  1. 是否有floating point类型不能作为模板参数的原因?这背后的原理是什么?我知道这在c++ 11之前是正确的,对于c++ 11标准来说似乎也是正确的。

  2. 为什么可以将pointerreference用于浮点类型作为非模板参数,而不是原始浮点类型?这里最大的区别是什么?

谢谢你的帮助。

为什么浮点类型不能用作模板形参?这背后的原理是什么?

虽然我不能给出最终的原因,但我可以肯定地想象,对接受浮点指针值作为参数的模板进行专门化会出现问题。

浮点数之间的相等比较是很复杂的(从某种意义上说,它有时会给出意想不到的结果),当匹配特化时,编译器必须在提供的实参和模板被特化的值之间执行相等检查。

另一个类似的问题是确定相同类模板的两个实例是否实际上是相同的类型:

template<double D>
struct X
{
    // ...
};
int main()
{
    X<3.0> x;
    X<some_constant_expression()> y;
}

xy是同一类的实例吗?为了确定这一点,必须在3.0some_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段:

两个模板id 引用同一个类或函数,如果

— [...]

—它们对应的整型或枚举型的非类型模板实参具有相同的值和

— [...]

—它们对应的非类型模板参数引用类型指向相同的外部对象或函数和

— [...]

(例子:

template<class E, int size> class buffer { / ... / };
buffer<char,2*512> x;
buffer<char,1024> y;

声明xy为同一类型,并且[…]

上面的例子表明,要确定同一个类模板的两个不同实例是否实际上是同一个类,需要在用作模板参数的常量表达式之间进行相等性检查。

要弄清楚这一点,请考虑整型和指针与它们的文字表示总是有一对一的关系。

然后让我们考虑一个非类型模板Foo<float>

假设它专为非二进制可表示的数字(如0.1: Foo<0.1>)而假设编译器根据专为符号名称进行修饰最终得到Foo_3DCCCCCC这样的符号因为编译器对IEEE 754 32位代表使用"四舍五入"

但是我们假设这段代码的用户是这样编译的,编译器选择"四舍五入到正无穷"。然后,专门化的名称改为Foo_3DCCCCCD,这是一个与之前在另一个翻译单元中专门化的函数完全不同的函数。

除非你开始制定各种各样的规则来处理所有这些事情(NaN、无穷大和其他非正常数字呢?),否则你就会让自己陷入不匹配和各种可能的问题。