【问题标题】:Is it a conforming compiler extension to treat non-constexpr standard library functions as constexpr?将非 constexpr 标准库函数视为 constexpr 是否符合编译器扩展?
【发布时间】:2026-01-16 15:10:01
【问题描述】:

gcc 编译以下代码而不发出警告:

#include <cmath>

struct foo {
  static constexpr double a = std::cos(3.);
  static constexpr double c = std::exp(3.);
  static constexpr double d = std::log(3.);
  static constexpr double e1 = std::asin(1.);
  static constexpr double h = std::sqrt(.1);
  static constexpr double p = std::pow(1.3,-0.75);
};

int main()
{
}

上面使用的标准库函数都不是constexpr 函数,我们可以在the draft C++11 standarddraft C++14 standard 都需要常量表达式 的地方使用它们部分7.1.5 [dcl.constexpr]

[...]如果它是由构造函数调用初始化的,则该调用应为 常量表达式(5.19)。否则,或者如果 constexpr 说明符是 在引用声明中使用,出现在 它的初始化器应该是一个常量表达式。[...]

即使使用 -std=c++14 -pedantic-std=c++11 -pedantic 也不会生成警告 (see it live)。使用 -fno-builtin 会产生错误 (see it live),这表明这些标准库函数的 builtin 版本被视为在 constexpr

虽然clang 不允许代码带有我尝试过的任何标志组合。

所以这是一个gcc 扩展,将至少一些内置函数视为constexpr 函数,即使标准没有明确要求它们是。我本来希望至少在严格一致性模式下收到警告,这是一个符合标准的扩展吗?

【问题讨论】:

    标签: c++ c++11 gcc language-lawyer c++14


    【解决方案1】:

    TL;DR

    在 C++14 中,这是明确不允许的,尽管在 2011 中,这种情况似乎是明确允许的。目前尚不清楚 C++11 是否属于 as-if rule,我不相信它会改变可观察到的行为,但我在下面引用的问题中没有阐明这一点。

    详情

    这个问题的答案随着LWG issue 2013 的不断发展而变化,其开头为:

    假设一个特定的函数在 标准,但是,在某些特定的实现中,它是可能的 在 constexpr 约束内编写它。如果实施者标记 像 constexpr 这样的函数是违反标准还是 它是符合标准的扩展吗?

    在 C++11 中,不清楚 as-if 规则 是否允许这样做,但原始提案一旦被接受就会明确允许这样做,我们可以在下面的 gcc 错误报告中看到 I参考,这是gcc团队的假设。

    在 2012 年达成的共识发生了变化,提案也发生了变化,在 C++14 中这是一个不符合标准的扩展。这反映在草案 C++14 标准部分 17.6.5.6 [constexpr.functions] 中,它说:

    [...]实现不应声明任何标准库函数 作为 constexpr 的签名,但明确表示的除外 需要。[..]

    虽然严格阅读这一点似乎为隐式处理内置函数留下了一些回旋余地,就好像它是一个 constexpr 我们可以从问题中的以下引用中看出,其目的是防止实现中的分歧,因为相同的代码可以使用 SFINAE 时会产生不同的行为(强调我的):

    在提交给全体委员会投票时表达了一些担忧 到 WP 状态,此问题已在没有足够的情况下得到解决 考虑到不同的库实现的后果,如 用户可以使用 SFINAE 来观察不同的行为 相同的代码

    我们可以从 gcc 错误报告 [C++0x] sinh vs asinh vs constexpr 中看到,该团队依赖于早先提出的 LWG 2013 解决方案,其中说:

    [...]此外,实现可以声明任何函数为 constexpr 如果该函数的定义满足必要 约束[...]

    在决定在严格一致性模式下是否允许对数学函数进行此更改时。

    据我所知,如果我们在严格一致性模式下收到警告,即使用-std=c++11 -pedantic,或者在此模式下禁用它,这将变得符合要求。

    请注意,我在错误报告中添加了一条注释,说明自从最初解决了这个问题后,解决方案发生了变化。

    Jonathan Wakely pointed out 在另一个问题中是一个更新的discussion 并且似乎可能会重新打开 gcc 错误报告以解决此一致性问题。

    内在函数呢

    Compiler intrinsics 不在标准范围内,据我所知,他们应该不受此规则的约束,因此使用:

    static constexpr double a = __builtin_cos(3.);
    

    应该被允许。这个问题出现在错误报告中,Daniel Krügler 的意见是:

    [...]库函数和其他内在函数可能被视为 例外,因为它们不需要被“解释” 正常的语言规则。

    【讨论】:

    • @Walter 我添加了一个LT;DR 部分。如果还有其他需要澄清的地方,请告诉我。
    • 内在函数呢?
    • 我认为谈论编译器内在函数没有帮助。所有以__ 开头的名称都保留给实现,并且实现定义了它们的语义。将特定于实现的函数的语义定义为 constexpr 是完全合理的,无论它们是否是内在函数,完全由实现者自行决定。
    • 而 as-if 规则当然不允许这样做。 As-if 只允许具有相同可观察行为的不同实现,而 SFINAE 更改是非常可观察的差异。
    • @BenVoigt 很好,内在函数的主题是在 gcc 错误报告以及此处的评论中提出的,因此即使答案看起来很明显,提及它似乎也很相关。我同意你对 as-if 规则的看法,我应该让它更明显,它现在已经更新了。