为什么在适用时不暗示constexpr? [英] Why isn't constexpr implied when applicable?

查看:79
本文介绍了为什么在适用时不暗示constexpr?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这些可能应该在不同的问题中,但是它们是相关的……


  1. 为什么我们要写 constexpr 呢?给定一组限制,编译器无法评估代码以查看其是否满足 constexpr 的要求,并将其视为 constexpr 是否这样做?作为纯粹的文档关键字,我不确定它是否成立,因为我无法想到我(其他人 constexpr 函数)应该真的在乎它是否在运行。


    这是我的逻辑:如果这是一个昂贵的函数,我认为按照惯例,我应该这样处理不管我是否给它编译时常量输入。这可能意味着在加载时调用它并保存结果,而不是在执行的关键时刻调用它。原因是因为 constexpr 实际上并没有向我保证它不会在一开始就在运行时执行-因此也许应该有一个新的/不同的机制来执行。



  2. constexpr 的限制似乎排除了很多(如果不是大多数的话)函数,这些函数在逻辑上可以我已经读过这至少部分(或全部?)是为了防止无限循环和挂起编译器。但是,如果这是 原因,那是否合法?



编译器是否能够针对使用给定输入的任何给定 constexpr 函数计算出是否无限循环?这不是解决问题的暂停问题的方法 em>输入。 constexpr 函数的输入是编译时间常数且是有限的,因此编译器只需要检查有限输入的无限循环:实际使用的输入。 如果您编写编译时无限循环,则应该是常规编译错误。

解决方案

I问了一个非常相似的问题,为什么我们需要将函数标记为constexpr?


何时我按下一位Clang作家Richard Smith,他解释说:


constexpr关键字确实有用。 / p>

它会影响何时实例化函数模板特化(如果在未求值的上下文中调用constexpr函数模板特化,则可能需要实例化;对于非constexpr函数,情况并非如此)调用永远不能成为常量表达式的一部分)。如果我们删除了关键字的含义,我们将不得不尽早实例化更多专业化特性,以防万一调用恰好是一个常量表达式。


它可以减少编译时间,限制实现在翻译期间尝试评估所需的函数调用集。 (这对于要求实现尝试常量表达式评估的情况很重要,但是如果这种评估失败,这不是错误,尤其是静态存储持续时间的对象的初始化程序。)



一开始这似乎并不令人信服,但是如果您仔细研究所有细节,那么在没有 constexpr 的情况下,事情确实会破裂。在使用ODR之前,无需实例化功能,这实际上意味着在运行时使用。 constexpr 函数的特殊之处在于它们可能会违反此规则,并且仍然需要实例化。


函数实例化是一个递归过程。实例化一个函数会导致实例化所使用的函数和类,而与任何特定调用的参数无关。


如果在实例化此依赖关系树时出了点问题(可能会付出巨大的代价),很难吞下该错误。此外,类模板实例化可能会在运行时产生副作用。


鉴于函数签名中依赖于参数的编译时函数调用,重载解析可能会导致实例化函数定义,而该实例仅是对函数定义的辅助。重载集中的函数,包括甚至不被调用的函数。这样的实例化可能会产生副作用,包括格式错误和运行时行为。


可以肯定的是,这是一个极端的情况,但是如果您不要求人们选择加入,则可能会发生不好的事情。 constexpr 函数。


对于 constexpr 对象,某些类型可以产生核心常量表达式,它们可以在常量表达式上下文中使用,而无需声明为 constexpr 。但是您真的不希望编译器在编译时尝试评估每个表达式。这就是持续传播的目的。另一方面,记录何时需要在编译时发生的必要文件似乎很必要。


These should probably be in different questions, but they're related so...

  1. Why do we need to write constexpr at all? Given a set of restrictions couldn't a compiler evaluate code to see if it satisfies the constexpr requirements, and treat it as constexpr if it does? As a purely documentation keyword I'm not sure it holds up because I can't think of a case where I (the user of someone else's constexpr function) should really care if it's run time or not.

    Here's my logic: If it's an expensive function I think as a matter of good practice I should treat it as such regardless of whether I give it compile-time constant input or not. That might mean calling it during load time and saving off the result, instead of calling it during a critical point in the execution. The reason is because constexpr doesn't actually guarantee to me that it will not be executed in run time in the first place — so perhaps a new/different mechanism should do that.

  2. The constexpr restrictions seem to exclude many, if not most, functions from being compile-time evaluated which logically could be. I've read this is at least in part (or perhaps wholly?) to prevent infinite looping and hanging the compiler. But, if this is the reason, is it legitimate?

Shouldn't a compiler be able to compute if, for any given constexpr function with the given inputs used, it loops infinitely? This is not solving the halting problem for any input. The input to a constexpr function is compile time constant and finite, so the compiler only has to check for infinite looping for a finite set of input: the input actually used. It should be a regular compilation error if you write a compile-time infinite loop.

解决方案

I asked a very similar question, Why do we need to mark functions as constexpr?

When I pressed Richard Smith, a Clang author, he explained:

The constexpr keyword does have utility.

It affects when a function template specialization is instantiated (constexpr function template specializations may need to be instantiated if they're called in unevaluated contexts; the same is not true for non-constexpr functions since a call to one can never be part of a constant expression). If we removed the meaning of the keyword, we'd have to instantiate a bunch more specializations early, just in case the call happens to be a constant expression.

It reduces compilation time, by limiting the set of function calls that implementations are required to try evaluating during translation. (This matters for contexts where implementations are required to try constant expression evaluation, but it's not an error if such evaluation fails -- in particular, the initializers of objects of static storage duration.)

This all didn't seem convincing at first, but if you work through the details, things do unravel without constexpr. A function need not be instantiated until it is ODR-used, which essentially means used at runtime. What is special about constexpr functions is that they can violate this rule and require instantiation anyway.

Function instantiation is a recursive procedure. Instantiating a function results in instantiation of the functions and classes it uses, regardless of the arguments to any particular call.

If something went wrong while instantiating this dependency tree (potentially at significant expense), it would be difficult to swallow the error. Furthermore, class template instantiation can have runtime side-effects.

Given an argument-dependent compile-time function call in a function signature, overload resolution may incur instantiation of function definitions merely auxiliary to the ones in the overload set, including the functions that don't even get called. Such instantiations may have side effects including ill-formedness and runtime behavior.

It's a corner case to be sure, but bad things can happen if you don't require people to opt-in to constexpr functions.

As for constexpr objects, certain types can produce core constant expressions which are usable in constant expression contexts without having been declared constexpr. But you don't really want the compiler to try evaluating every single expression at compile time. That's what constant propagation is for. On the other hand it seems pretty essential to document when something needs to happen at compile time.

这篇关于为什么在适用时不暗示constexpr?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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