c++ - 浮点类型作为 C++20 中的模板参数

标签 c++ templates floating-point c++20

根据 cppreference C++20 现在支持模板中的浮点参数。
但是,我无法在该站点以及其他站点上找到任何编译器支持信息。
当前的gcc主干只是这样做,其他的都是负面的。
我只想知道这是否是一个非常低优先级的功能和/或何时会得到普遍支持。
我能找到的唯一相关的事情是:
P0732R2 非类型模板参数中的类类型。如果有人可以简单地解释一下,那就太好了。

最佳答案

似乎可以在这里回答的真正问题是关于此功能的 历史记录 ,以便可以在上下文中理解任何编译器支持。
非类型模板参数类型的限制
人们已经 wanting class-type non-type template parameters 很久了。那里的答案有些欠缺;真正使对此类模板参数(实际上,非平凡的用户定义类型)的支持变得复杂的是它们未知的 标识 概念:给定

struct A {/*...*/};
template<A> struct X {};
constexpr A f() {/*...*/}
constexpr A g() {/*...*/}
X<f()> xf;
X<g()> &xg=xf;  // OK?
我们如何确定 X<f()>X<g()> 是否是同一类型?对于整数,答案似乎很直观,但类类型可能类似于 std::vector<int> ,在这种情况下我们可能有
// C++23, if that
using A=std::vector<int>;
constexpr A f() {return {1,2,3};}
constexpr A g() {
  A ret={1,2,3};
  ret.reserve(1000);
  return ret;
}
尽管有非常不同的 行为 (例如,迭代器失效),但不清楚如何理解两个对象包含相同的值(因此与 == 比较相等)。
P0732 非类型模板参数中的类类型
确实,本文首先在新的 <=> 运算符方面添加了对类类型非类型模板参数的支持。逻辑是默认操作符的类是“对比较透明的”(使用的术语是“强结构相等”),因此程序员和编译器可以就身份的定义达成一致。
P1185 <=> != ==后来意识到 == 出于性能原因应该单独默认(例如,它允许提前退出以比较不同长度的字符串),并且根据该运算符重写了强结构相等性的定义(随附免费提供)默认的 <=> )。这不会影响这个故事,但没有它,这条线索是不完整的。
P1714 NTTP 没有 float、double 和 long double 是不完整的!
发现类类型的 NTTP 和 constexpr std::bit_cast 的不相关特性允许将浮点值走私到 std::array<std::byte,sizeof(float)> 之类的类型中的模板参数中。由这种技巧产生的语义将是 float 的每个 表示 将是​​不同的模板参数,尽管 -0.0==0.0 和(给定 float nan=std::numeric_limits<float>::quiet_NaN(); )nan!=nan 。因此建议直接允许浮点值作为模板参数,具有这些语义,以避免鼓励广泛采用这种hacky 解决方法。
当时,关于(给定 template<auto> int vt; )x==y 可能与 &vt<x>==&vt<y> 不同)的想法存在很多混淆,并且提案是 被拒绝 因为需要更多的分析而不是 C++20 所能提供的。
P1907R0 与非类型模板参数不一致
原来==在这方面有很多问题。即使是 枚举 (一直被允许作为模板参数类型)也可以重载 == ,并且将它们用作模板参数会完全忽略该重载。 (这或多或少是必要的:这样的操作符可能在某些翻译单元中定义而不是在其他翻译单元中定义,或者可能定义不同,或者具有内部链接等。)此外,实现需要对模板参数做的是规范化 它:将一个模板参数(例如,调用)与另一个模板参数(例如,显式特化)进行比较将要求后者已经以某种方式根据前者进行了识别,同时又以某种方式允许它们的可能性可能不同。
这种身份的概念已经不同于其他类型的 == 。甚至 P0732 也认识到 引用 (也可以是模板参数的类型)不会与 == 进行比较,因为当然 x==y 并不暗示 &x==&y 。不太受欢迎的是 成员指针 也违反了这种对应关系:由于它们在常量评估中的不同行为,尽管比较 == 和指向成员的指针,但它们在常量评估中的行为不同,作为模板参数是不同的被强制转换为指向基类具有相似的行为(尽管它们的比较是未指定的,因此不允许作为常量评估的直接组成部分)。
事实上,在 2019 年 11 月,GCC 已经实现了对类类型 NTTP 的基本支持,不需要任何比较运算符。
P1837 从 C++20 中删除类类型的 NTTP
这些不协调是如此之多,以至于已经提出将整个特性 推迟到 C++23 的 。面对如此流行的功能中的这么多问题,委托(delegate)一个小组指定保存它所需的重大更改。
P1907R1(结构类型)
这些关于类类型和浮点类型模板参数的故事在 P1907R0 的修订版中重新出现,该修订版保留了它的名称,但用同样针对同一主题提交的 National Body 评论的解决方案替换了它的正文。 (新)想法是认识到比较从来没有真正密切相关,并且模板参数同一性的唯一一致模型是,如果在不断评估期间有任何方法可以区分两个参数(具有上述权力),则两个参数是不同的区分指向成员的指针等)。毕竟,如果两个模板参数产生相同的特化,则该特化必须具有一个行为,并且它必须与直接使用任一参数获得的行为相同。
虽然支持广泛的类类型是可取的,但几乎在最后一刻为 C++20 引入(或更确切地说是重写)的新特性可以可靠地支持的唯一类型是那些每个值可以由实现区分的可以由其客户端区分 - 因此,只有那些具有所有 公共(public) 成员(递归地具有此属性)。对此类结构类型的限制不如对骨料的限制那么强,因为只要是 constexpr,任何构造过程都是允许的。它还为 future 的语言版本提供了合理的扩展,以支持更多的类类型,甚至可能是 std::vector<T> ——同样,通过规范化(或序列化)而不是通过比较(不能支持此类扩展)。
一般解决方案
这种新发现的理解与 C++20 中的其他任何东西都没有关系;使用此模型的类类型 NTTP 可能是 C++11 (引入了类类型的常量表达式)的一部分。支持立即扩展到 union ,但逻辑根本不限于类;它还确定长期禁止模板参数是指向子对象的指针或具有浮点类型的模板参数也是出于对 == 的混淆,并且是不必要的。 (虽然出于技术原因这不允许字符串文字作为模板参数,但它允许指向静态字符数组第一个字符的 const char* 模板参数。)
换句话说,激励 P1714 的力量最终被认为是模板基本行为的不可避免的数学后果,毕竟浮点模板参数成为 C++20 的一部分。然而,它们的原始提案实际上并没有为 C++20 指定浮点和类类型 NTTP,这使得“编译器支持”文档变得复杂。

关于c++ - 浮点类型作为 C++20 中的模板参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63694879/

相关文章:

c++ - 如何将 sf::RenderWindow 传递给另一个函数?

C++: 错误: ‘class’ 没有名为的成员

c++ - 计算常量的函数的编译时评估

java - 浮点不准确会导致 Math.random() 或 new Random().nextFloat() 实际上返回值 <0 或 >=1 吗?

c++ - float 的整数部分中的 10 进制数字的最大位数是多少

c - 浮点值与另一个翻译单元中使用的函数不同

C++从十六进制字符串转换为int时出错

c++ - 如何检测模板参数是否为 noexcept 函数?

c++ - 定义基于模板的映射指针,以便在验证范围时将 int 解析为 enum

c++ - 如何使用 SFINAE 从 C++11 中的多个选项中选择构造函数