【问题标题】:understanding of non-type template parameters非类型模板参数的理解
【发布时间】:2013-05-22 14:08:28
【问题描述】:

根据 C++11 标准 N3485 第 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类型不能作为模板参数有什么原因吗?这背后的基本原理是什么?我知道在 C++11 之前是这样,对于 C++11 标准来说似乎也是如此。

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

感谢您的帮助。

【问题讨论】:

标签: c++ templates c++11


【解决方案1】:

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

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

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

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

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 段:

两个 template-ids 引用同一个类或函数,如果

——[...]

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

——[...]

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

——[...]

[ 例子:

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

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

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

【讨论】:

  • +1 谢谢你,安迪。编译器总是做equality checking between the argument provided and the value for which a template is being specialized. 吗?
  • 我不会说浮点比较很棘手,至少对于编译器部分来说不是。其实很简单。但它通常会给出对人类来说非常令人惊讶的结果,因此使用浮点模板对于 devolpers 来说只是一个完全不可预测的事情。
  • @ArneMertz:是的,这就是我的意思——我可能不得不改写
  • @taocp:是的,尽管允许进行一些转换(参见 14.3.2/5)
  • @juanchopanza:它们似乎被当作指针对待(参见我添加的示例)
【解决方案2】:

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

但是让我们考虑一个非类型模板Foo&lt;float&gt;

假设它专门用于像0.1Foo&lt;0.1&gt; 这样的不可二进制表示的数字,假设编译器根据专业化修饰符号名称,你最终会得到类似Foo_3DCCCCCC 的东西,因为编译器是对 IEEE 754 32 位代表使用“四舍五入”。

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

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

【讨论】:

  • 非常感谢,这些拓宽了我对tempalte的知识范围
猜你喜欢
  • 2021-03-18
  • 1970-01-01
  • 1970-01-01
  • 2011-08-06
  • 2014-05-18
  • 2019-04-24
相关资源
最近更新 更多