C++20中是否不再允许针对程序定义类型在std中进行函数模板的专门化?

28

引用自cppreference.com:

添加模板特化

只有当声明依赖于至少一个程序定义的类型,而且特化满足所有原始模板的要求时(除了禁止此类特化的情况),才允许向std命名空间添加任何标准库|类模板(自从C++20起)|的模板特化。

这是否意味着,从C++20开始,不再允许为用户定义的类型将函数模板的特化添加到std命名空间中?如果是这样,那么许多现有代码都可能会出现问题,是吗?(这似乎是一个“激进”的变化。)而且,它将使这样的代码产生未定义的行为,这将不会触发编译错误(但希望能生成警告)。


删除了我之前的评论,我会相信 https://en.cppreference.com/w/cpp/language/extending_std - Matthieu Brucher
你是否曾经想过为 std::function 提供一个专门化的实现? - 463035818_is_not_a_number
@user463035818 在 std:: 命名空间中编写函数,而不是使用类模板 std::function - Caleth
1
@Caleth 误读了标题 ;) - 463035818_is_not_a_number
2个回答

28
的确,目前看起来是这样的。之前[namespace.std]包含了以下内容:

除非声明依赖于用户定义的类型且特化符合原始模板的标准库要求并且未被明确禁止,程序可以为任何标准库模板向 std 命名空间添加模板特化。

当前的草案说明如下:

除非明确禁止,程序可以向 std 命名空间添加任何标准库类模板的模板特化,条件是:(a) 添加的声明依赖于至少一个程序定义的类型,并且(b) 特化符合原始模板的标准库要求。

强调由我

看起来是由 Walter E. Brown 的论文Thou Shalt Not Specialize std Function Templates!所致。在其中,他详细介绍了许多应该改变此规则的理由,例如:

  • Herb Sutter:“特化不参与重载。如果你想自定义一个函数基本模板,并希望该自定义参与重载决策(或总是被选择),则必须对函数进行包装。”
  • Barry Revzin:“模板特化是代码的二次来源。根据Kevlin Henney的说法,二次来源是邪恶的根源之一。”
  • Walter E. Brown:“在C ++中只有一种方法可以真正自定义一个库内的函数模板:将其命名为不同名称并使用额外的参数来指定要定制的类型或值。”
  • Herb Sutter:在函数模板中,如果您想提供一个完全匹配(即精确匹配)的重载,请将其作为普通函数而不是特化。如果您确实提供了重载,请避免提供特化。”
  • David Abrahams:“使用函数模板特化是错误的,因为它与重载产生不良交互。例如,如果您为std :: vector<mytype>&特化常规的std :: swap,则在重载解析过程中,您的特化不会被选择为标准的向量专用swap,因为在重载解析期间不考虑特化。”
  • Howard Hinnant:“这个问题已经解决很长时间了……请忽略Dave在这个领域的专业意见/答案,后果自负。”
  • Eric Niebler:“由于C++以模板方式解析函数调用的明显古怪方式……我们对swap进行非限定调用,以查找可能定义在相关命名空间中的重载……我们使用using std :: swap ,以防万一没有这样的重载,我们会找到在std名称空间中定义的默认版本。”
  • High Integrity C ++编码标准:“重载解析不考虑函数模板的显式特化。只有在重载解析选择了函数模板之后,才会考虑任何显式的特化。”

你的草稿引用只涉及到类模板,而没有涉及到函数模板。 - jonspaceharper
@JonHarper,很抱歉我不理解这个问题。原文曾经说“程序可以将任何标准库模板的模板特化添加到std命名空间中”,现在它通过说明“程序可以将任何标准库类模板的模板特化添加到命名空间中”来限制了这一点。 - NathanOliver
只是指出修改不是引文的一部分,即没有参考先前的措辞,使其不清楚。 - jonspaceharper
1
@JonHarper 好的。我已经更新了答案,以展示当前标准文本进行比较。 - NathanOliver

16

这个变化并不是非常激进。这个变化来源于Walter E. Brown的论文。该论文详细介绍了其基本原理,但最终可以归结为以下几点:

  1. 作为自定义点,函数模板的专业化效果相当差。在这方面,重载和ADL效果要好得多。论文还讨论了其他自定义点。
  2. 标准库并没有过度依赖这种较差的自定义点。
  3. 实施的措辞变化允许将整个声明添加到命名空间std中(不仅仅是特殊化),只要明确允许即可。因此现在有了更好的自定义点。

考虑到#1和#2,现有代码很可能不会出现问题。或者至少不会成为一个主要问题。使用autoregister的代码也曾经“破裂”,但那微不足道的C++代码并没有阻碍进步。


等等,那么我们将能够向std元组添加begin重载吗? - Yakk - Adam Nevraumont
@Yakk-AdamNevraumont - 我不确定这是否可能在“明确允许”的范围内。我怀疑大多数这些允许将涉及范围库及其自定义点。 - StoryTeller - Unslander Monica
1
它绝对不允许向std添加重载。 - T.C.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接