浮点类型作为 C++20 中的模板参数 [英] Floating-point types as template parameter in C++20

查看:41
本文介绍了浮点类型作为 C++20 中的模板参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

根据 cppreference C++20 现在支持浮动- 模板中的点参数.但是,我无法在该站点以及其他站点上找到任何编译器支持信息.当前的gcc主干只是这样做,其他都是负面的.

According to cppreference C++20 now supports floating-point parameters in templates. I am, however, unable to find any compiler support information on that site as well as on others. Current gcc trunk just does it, the others are negative.

我只想知道这是否是一个优先级非常低的功能和/或何时会得到普遍支持.

I would just like to know if this is a very low-priortity feature and/or when to expect it to become commonly supported.

我能找到的唯一相关内容是:P0732R2 非类型模板参数中的类类型.如果有人能简单地解释一下,那就太好了.

The only related thing I can find is: P0732R2 Class types in non-type template parameters. Kudos if anyone could briefly explain that instead.

推荐答案

看来真正可以在这里回答的问题是关于这个特性的历史,这样无论编译器支持什么都可以在上下文中理解.

It seems that the real question that can be answered here is about the history of this feature, so that whatever compiler support can be understood in context.

人们想要类类型非类型模板参数已经很久了.那里的答案有些欠缺;真正使支持此类模板参数(实际上,非平凡的用户定义类型)变得复杂的是它们未知的身份概念:给定

People have been wanting class-type non-type template parameters for a long time. The answers there are somewhat lacking; what really makes support for such template parameters (really, of non-trivial user-defined types) complicated is their unknown notion of identity: given

struct A {/*...*/};
template<A> struct X {};
constexpr A f() {/*...*/}
constexpr A g() {/*...*/}
X<f()> xf;
X<g()> &xg=xf;  // OK?

我们如何确定XX 是否是同一类型?对于整数,答案似乎很直观,但类类型可能类似于 std::vector,在这种情况下,我们可能有

how do we decide whether X<f()> and X<g()> are the same type? For integers, the answer seems intuitively obvious, but a class type might be something like std::vector<int>, in which case we might have

// 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;
}

并且不清楚如何解释两个对象包含相同的(因此与==比较)的事实,尽管有很大不同行为(例如,用于迭代器失效).

and it's not clear what to make of the fact that both objects contain the same values (and hence compare equal with ==) despite having very different behavior (e.g., for iterator invalidation).

确实,这篇论文首先在新的 <=> 运算符方面添加了对类类型非类型模板参数的支持.逻辑是默认该运算符的类是对比较透明的".(使用的术语是强结构相等"),因此程序员和编译器可以就身份的定义达成一致.

It's true that this paper first added support for class-type non-type template parameters, in terms of the new <=> operator. The logic was that classes that defaulted that operator were "transparent to comparisons" (the term used was "strong structural equality") and so programmers and compilers could agree on a definition of identity.

后来意识到 == 出于性能原因应该单独默认(例如,它允许提前退出以比较不同长度的字符串),并且定义强结构相等性的重写根据该运算符(它与默认的 <=> 一起免费提供).这不会影响这个故事,但没有它,这条线索是不完整的.

Later it was realized that == should be separately defaultable for performance reasons (e.g., it allows an early exit for comparing strings of different lengths), and the definition of strong structural equality was rewritten in terms of that operator (which comes for free along with a defaulted <=>). This doesn't affect this story, but the trail is incomplete without it.

发现类类型 NTTP 和 constexpr std::bit_cast 允许将浮点值走私到像 std::array<std::byte 这样类型的模板参数中,sizeof(float)>.由这种技巧产生的语义是 float 的每个 表示 都是不同的模板参数,尽管 -0.0==0.0 和(给定 float nan=std::numeric_limits::quiet_NaN();)nan!=nan.因此,有人提议允许浮点值直接作为具有这些语义的模板参数,以避免鼓励广泛采用这种笨拙的变通方法.

It was discovered that class-type NTTPs and the unrelated feature of constexpr std::bit_cast allowed a floating-point value to be smuggled into a template argument inside a type like std::array<std::byte,sizeof(float)>. The semantics that would result from such a trick would be that every representation of a float would be a different template argument, despite the fact that -0.0==0.0 and (given float nan=std::numeric_limits<float>::quiet_NaN();) nan!=nan. It was therefore proposed that floating-point values be allowed directly as template arguments, with those semantics, to avoid encouraging widespread adoption of such a hacky workaround.

当时,关于(给定 template int vt;)x==y 可能不同于 &vt<x>==&vt<y>),并且该提案被拒绝,因为需要进行比 C++20 所能承受的更多分析.

At the time, there was a lot of confusion around the idea that (given template<auto> int vt;) x==y might differ from &vt<x>==&vt<y>), and the proposal was rejected as needing more analysis than could be afforded for C++20.

事实证明==在这方面有很多问题.甚至枚举(一直被允许作为模板参数类型)也可以重载==,并且将它们用作模板参数会完全忽略该重载.(这或多或少是必要的:这样的操作符可能在某些翻译单元中定义而不是在其他翻译单元中定义,或者可能定义不同,或者具有内部链接,.)此外,实现需要什么对模板参数的处理是规范化:将一个模板参数(例如调用)与另一个模板参数(例如,显式特化)进行比较需要后者以某种方式已经被识别,同时又以某种方式允许它们不同的可能性.

It turns out that == has a lot of problems in this area. Even enumerations (which have always been allowed as template parameter types) can overload ==, and using them as template arguments simply ignores that overload entirely. (This is more or less necessary: such an operator might be defined in some translation units and not others, or might be defined differently, or have internal linkage, etc.) Moreover, what an implementation needs to do with a template argument is canonicalize it: to compare one template argument (in, say, a call) to another (in, say, an explicit specialization) would require that the latter had somehow already been identified in terms of the former while somehow allowing the possibility that they might differ.

对于其他类型,这种标识的概念已经不同于 ==.甚至 P0732 也认识到 references(也可以是模板参数的类型)不会与 == 进行比较,因为当然 x==y 并不意味着 &x==&y.不太受欢迎的是,指向成员的指针也违反了这种对应关系:由于它们在常量评估中的行为不同,指向联合的不同成员的指针作为模板参数是不同的尽管比较 ==,并且已被强制转换为指向基类的成员指针具有类似的行为(尽管它们的比较未指定,因此不允许作为常量评估的直接组件).

This notion of identity already differs from == for other types as well. Even P0732 recognized that references (which can also be the type of template parameters) aren't compared with ==, since of course x==y does not imply that &x==&y. Less widely appreciated was that pointers-to-members also violate this correspondence: because of their different behavior in constant evaluation, pointers to different members of a union are distinct as template arguments despite comparing ==, and pointers-to-members that have been cast to point into a base class have similar behavior (although their comparison is unspecified and hence disallowed as a direct component of constant evaluation).

事实上,在 2019 年 11 月,GCC 已经实现了对类类型 NTTP 的基本支持,不需要任何比较运算符.

In fact, in November 2019 GCC had already implemented basic support for class-type NTTPs without requiring any comparison operator.

这些不协调之处太多了,以至于已经有人提议将整个功能推迟到 C++23.面对如此受欢迎的功能中的这么多问题,委托一个小组指定保存它所需的重大更改.

These incongruities were so numerous that it had already been proposed that the entire feature be postponed until C++23. In the face of so many problems in so popular a feature, a small group was commissioned to specify the significant changes necessary to save it.

这些关于类类型和浮点类型的模板参数的故事在 P1907R0 的修订版中重新融合,该修订版保留了它的名称,但用一个解决方案替换了它的主体,以解决就同一主题提交了申请.(新)想法是认识到比较从来没有真正密切相关,并且模板参数同一性的唯一一致模型是如果在不断评估期间有任何方法区分它们,则两个参数是不同的(它具有上述区分成员指针的能力,).毕竟,如果两个模板参数产生相同的特化,则该特化必须具有一个行为,并且它必须与直接使用任一参数获得的行为相同.

These stories about template arguments of class type and of floating-point type reconverge in the revision of P1907R0 which retained its name but replaced its body with a solution to National Body comments that had also been filed on the same subject. The (new) idea was to recognize that comparisons had never really been germane, and that the only consistent model for template argument identity was that two arguments were different if there was any means of distinguishing them during constant evaluation (which has the aforementioned power to distinguish pointers-to-members, etc.). After all, if two template arguments produce the same specialization, that specialization must have one behavior, and it must be the same as would be obtained from using either of the arguments directly.

虽然支持广泛的类类型是可取的,但几乎在 C++20 的最后一刻引入(或重写)的新特性能够可靠地支持的只有那些其中,实现可以区分的每个值都可以由其客户端区分——因此,只有那些具有所有 public 成员(递归地具有此属性)的值.对此类结构类型的限制不如对聚合的限制那么强,因为只要是 constexpr,任何构造过程都是允许的.它还为未来的语言版本提供了合理的扩展,以支持更多的类类型,甚至可能是 std::vector——同样,通过规范化(或序列化)而不是通过比较(不能支持此类扩展)).

While it would be desirable to support a wide range of class types, the only ones that could be reliably supported by what was a new feature introduced (or rather rewritten) at almost the last possible moment for C++20 were those where every value that could be distinguished by the implementation could be distinguished by its clients—hence, only those that have all public members (that recursively have this property). The restrictions on such structural types are not quite as strong as those on an aggregate, since any construction process is permissible so long as it is constexpr. It also has plausible extensions for future language versions to support more class types, perhaps even std::vector<T>—again, by canonicalization (or serialization) rather than by comparison (which cannot support such extensions).

这种新发现的理解与 C++20 中的任何其他内容都没有关系;使用此模型的类类型 NTTP 可能是 C++11(引入类类型的常量表达式)的一部分.支持立即扩展到联合,但逻辑根本不限于类;它还确定长期禁止模板参数是指向子对象的指针或具有浮点类型的模板参数也是出于对 == 的混淆,并且是不必要的.(虽然出于技术原因,这不允许字符串文字作为模板参数,但它确实允许 const char* 模板参数指向静态字符数组的第一个字符.)

This newfound understanding has no relationship to anything else in C++20; class-type NTTPs using this model could have been part of C++11 (which introduced constant expressions of class type). Support was immediately extended to unions, but the logic is not limited to classes at all; it also established that the longstanding prohibitions of template arguments that were pointers to subobjects or that had floating-point type had also been motivated by confusion about == and were unnecessary. (While this doesn't allow string literals to be template arguments for technical reasons, it does allow const char* template arguments that point to the first character of static character arrays.)

换句话说,激发 P1714 的力量最终被认为是模板基本行为的不可避免的数学后果,毕竟浮点模板参数成为 C++20 的一部分.然而,它们的原始提案实际上并没有为 C++20 指定浮点和类类型 NTTP,这使得编译器支持"变得复杂.文档.

In other words, the forces that motivated P1714 were finally recognized as inevitable mathematical consequences of the fundamental behavior of templates and floating-point template arguments became part of C++20 after all. However, neither floating-point nor class-type NTTPs were actually specified for C++20 by their original proposals, complicating "compiler support" documentation.

这篇关于浮点类型作为 C++20 中的模板参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆