12

我在理解以下段落时遇到问题 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

—结束示例]

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

  1. floating point为什么type 不能用作模板参数是有原因的吗?这背后的理由是什么?我知道这在 C++11 之前是正确的,对于 C++11 标准似乎也是如此。

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

谢谢您的帮助。

4

2 回答 2

8

浮点类型不能用作模板参数是否有原因?这背后的理由是什么?

虽然我无法给出最终原因,但我可以肯定地想象,专门接受一个浮点值作为参数的模板会出现问题。

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

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

template<double D>
struct X
{
    // ...
};

int main()
{
    X<3.0> x;
    X<some_constant_expression()> y;
}

xy实例是同一个类吗?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 段:

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

— [...]

— 它们对应的整数或枚举类型的非类型模板参数具有相同的值,并且

— [...]

— 它们对应的引用类型的非类型模板参数引用相同的外部对象或函数,并且

— [...]

[示例:

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

声明xy是同一类型,并且 [...]

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

于 2013-05-22T14:20:05.867 回答
2

要弄清楚这一点,请考虑整数类型和指针与其字面表示始终具有一对一的关系。

但是,让我们考虑一个非类型模板Foo<float>

假设它专门用于非二进制可表示的数字,例如0.1:Foo<0.1>并且假设编译器根据专门化修饰符号名称,您最终会得到类似Foo_3DCCCCCC的东西,因为编译器对 IEEE 754 32 位代表使用“向下取整” .

但是,假设此代码的用户正在编译,编译器会选择“舍入到正无穷大”。然后,专门化的名称改为Foo_3DCCCCCD与先前专门用于另一个翻译单元的功能完全不同的功能。

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

于 2013-05-22T14:46:21.310 回答