【问题标题】:Would it be sufficient for constexpr, consteval, and constinit to be definitions instead of keywords?constexpr、consteval 和 constinit 是定义而不是关键字就足够了吗?
【发布时间】:2021-11-08 16:01:15
【问题描述】:

编译时关键字的规则:constexprconstevalconstinit 的定义似乎足够好,编译器会在您误用标签时发出警告。

编译器(很像内联)可以在所有地方应用规则来确定代码是否实际上可以应用编译时关键字之一,并且根据语言规范强制应用,这是有道理的,尽可能多地编译,就像应用了编译时关键字一样。

或者,至少,如果将编译时关键字应用于函数,并且应用了正确的编译时关键字,则代码将符合条件。编译该函数,就好像所有调用的函数都有正确的编译时关键字一样。

这是一个简单的例子:

#include <tuple>

consteval std::tuple<int, int> init1(int x, int y)
{
    return {x,y};
}

std::tuple<int, int>& foo1()
{
    static constinit std::tuple<int, int> x=init1(1,2);
    return x;
}

std::tuple<int, int> init2(int x, int y)
{
    return {x,y};
}

std::tuple<int, int>& foo2()
{
    static std::tuple<int, int> x=init2(1,2);
    return x;
}

static std::tuple<int, int> x3=init2(1,2);

std::tuple<int, int>& foo3()
{
    return x3;
}

在这里查看:https://godbolt.org/z/KrzGfnEo7

请注意,init1/foo1 完全使用编译时关键字指定,并且不需要保护变量,因为它已完全初始化(不仅仅是 0 填充)。

问题是为什么编译器在 init2/foo2 或 x3/foo3 的情况下不做同样的事情?或者更确切地说,为什么允许编译器在编译时不完全初始化。

需要编辑(根据评论)请参阅 (Why do we need to mark functions as constexpr?) constexpr。我更关心constevalconstinit 的情况。是否可以修改规范以要求在编译时尝试解析代码并且不会因为缺少constexpr 而失败?这将允许接口合约根据相关问题仅使用 constexpr

【问题讨论】:

  • 你在问为什么constexpr没有被自动应用? (阅读stackoverflow.com/q/14472359/103167)或者编译器是否可以对不使用constexpr的代码执行编译时优化? (可以。)
  • 是的,我明白,因为 constexpr 是可选的。我更关心我应用 consteval 或 constinit 并且编译器可以允许它的地方,而与 constexpr 等的存在无关,如果不清楚,抱歉。我会看看是否可以进行编辑以使其更清晰。
  • 允许编译器应用任何优化,只要您无法区分。当一个函数可以在编译时评估时,为什么不应该呢?见stackoverflow.com/questions/15718262/…
  • 问题不是“编译器可以吗?”,而是“为什么不需要编译器?”
  • @GlennTeitelbaum:“是否可以修改规范以要求在编译时尝试解析代码并且不会因为缺少constexpr而失败?”实现质量的问题,而不是语言规范。

标签: c++ language-lawyer c++20


【解决方案1】:

或者,至少,如果将编译时关键字应用于函数,并且如果应用了正确的编译时关键字,代码将符合条件。

您的问题的基础是假设这些关键字只是主题的变体,可能包含其中一些关键字的函数应该具有基于函数内属性的特定关键字一个人。

这不合理。

对于函数,任何可以是constexpr 的函数也可以是consteval。不同之处在于,一个可以在常量表达式求值期间调用,而另一个必须在常量表达式求值期间调用。这不是函数定义的内在属性;它是关于如何使用该功能

编译器不应仅仅因为函数定义恰好对于consteval 也是合法的,就覆盖了程序员能够在运行时使用函数的决定。

还应注意,consteval 函数的函数定义要求与constexpr 函数的相同

也不可能知道函数定义是否应该是constexpr。请记住:虽然constexpr 函数explicitly forbids certain constructs from appearing in the definition,但它也允许您拥有certain constructs in the definition that aren't allowed to be evaluated during constant evaluation...,只要您在持续评估期间从未真正允许评估这些代码路径。所以有许多函数定义恰好对constexpr 有效,即使程序员不打算在编译时对它们进行评估。

Lambda 获得隐含的 constexpr 定义只是因为 lambda 表达式中的空间非常宝贵。

对于变量,隐式constexpr 更没有意义。如果您打算稍后在常量表达式中使用变量,则该变量应该是 constexpr。这要求它有一个作为常量表达式的初始化程序。隐式 constexpr 是一个问题,因为您可能开始依赖隐式 constexpr 规则,然后将初始化程序更改为不再是常量表达式,并在使用其 constexpr 属性的地方出现奇怪的错误。

最好让用户提前明确,而不是误判用户试图做的事情,让用户认为某事在不希望的情况下应该是有效的。

constinit 只是为了确保用户永远不会意外地使用非常量表达式来初始化变量。将其设为隐式绝对没有意义,因为将表达式更改为不再是常量表达式不会导致编译失败。编译器足够聪明,可以知道何时使用常量表达式初始化变量,并可以自行决定如何处理。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-03-08
    • 1970-01-01
    • 1970-01-01
    • 2018-11-03
    • 1970-01-01
    • 2011-01-05
    • 2023-01-24
    • 1970-01-01
    相关资源
    最近更新 更多