【问题标题】:What does it mean to "poison a function" in C++?在 C++ 中“毒化函数”是什么意思?
【发布时间】:2015-11-27 20:48:20
【问题描述】:

在 Scott Schurr 的演讲 "Introducing constexpr" at CppCon 的最后,他问“有没有办法毒化函数”?然后他解释说,这可以通过以下方式完成(尽管以非标准方式):

  1. throw 放入constexpr 函数中
  2. 声明一个未解决的extern const char*
  3. throw 中引用未解决的extern

我觉得我在这里有点超出我的深度,但我很好奇:

  • “毒化函数”是什么意思?
  • 他概述的技术有什么意义/用处?

【问题讨论】:

  • 从来没有听说过这个词,请用一个简洁的例子澄清一下!
  • @πάνταῥεῖ,我刚刚澄清了。这是一个“在小圈子里广为人知”的术语
  • 他说的是确保每次调用 constexpr 函数都在编译时进行评估。
  • @T.C.对——他提到constexpr 函数既可以在编译时使用,也可以在运行时使用。所以这是一种强制它的方法,这样你就不能在运行时使用它了?什么时候有用?
  • 特别是在 C++11 中,constexpr 函数通常不是最有效的实现,因为存在限制,因此可能不希望在运行时对其进行评估;或者,也许是错误情况(如他的示例)。

标签: c++ constexpr


【解决方案1】:

通常是指使函数无法使用,例如如果您想禁止在程序中使用动态分配,您可以“毒化”malloc 函数,使其无法使用。

在视频中,他以更具体的方式使用它,如果您阅读显示的幻灯片,当他谈到中毒功能时,就会清楚这一点,上面写着“仅强制编译时的方法?”

所以他说的是“毒化”函数以使其在运行时不可调用,因此它只能在常量表达式中调用。该技术是在函数中拥有一个在编译时上下文中调用时永远不会采用的分支,并使该分支包含会导致错误的内容。

throw 表达式在 constexpr 函数中是允许的,只要它在函数的编译时调用期间永远不会到达(因为你不能在编译时抛出异常,它本质上是动态操作,比如分配内存)。因此,引用未定义符号的 throw 表达式将不会在编译时调用期间使用(因为这将无法编译)并且不能在运行时使用,因为未定义符号会导致链接器错误。

因为未定义的符号在函数的编译时调用中不是“odr-used”,实际上编译器不会创建对该符号的引用,所以它是未定义的也没关系。

这有用吗?他在演示如何 这样做,不一定说这是一个好主意或广泛有用。如果您出于某种原因需要这样做,那么他的技术可能会解决您的问题。如果您不需要它,则无需担心。

可能有用的一个原因是,当某些操作的编译时版本没有达到应有的效率时。 constexpr 函数中允许的表达式类型存在限制(特别是在 C++11 中,C++14 中删除了一些限制)。因此,您可能有两个版本的函数用于执行计算,一个是最佳的,但使用了 constexpr 函数中不允许的表达式,另一个是有效的 constexpr 函数,但如果在运行时调用会表现不佳-时间。您可以毒化次优版本以确保它永远不会用于运行时调用,从而确保将更有效的(非 constexpr)版本用于运行时调用。

注意在编译时使用的 constexpr 函数的性能并不重要,因为它无论如何都没有运行时开销。它可能会通过让编译器做额外的工作来减慢你的编译速度,但它不会有任何运行时性能成本。

【讨论】:

  • 我确实阅读了幻灯片的文字,但我没有看到与他使用的术语的联系。现在很明显,你已经解释过了,但我当时没有看到。非常感谢这个出色的答案——我就是喜欢这个网站。
  • @PravasiMeet,问你自己的问题,不要劫持别人关于不同事物的问题。一个简单的解决方案是在每个翻译单元中将其定义为已删除,或者将其替换为您自己的引用未定义符号的定义。
【解决方案2】:

“中毒”标识符意味着在“中毒”之后对标识符的任何引用都是硬编译器错误。例如,此技术可用于硬弃用(函数已弃用,永远不要使用它!)。

在 GCC 中,传统上对此有一个编译指示:#pragma GCC poison

【讨论】:

  • 是的,但不完全是那个演讲中使用的意义。
  • @T.C.,好的,我应该在回答之前先看看它:)
猜你喜欢
  • 1970-01-01
  • 2020-12-12
  • 2013-03-09
  • 2020-07-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多