constexpr 函数调用无法编译

Dmi*_*ano 5 c++

为什么下面的代码不能编译?

#include <stdint.h>
#include <array>

class A
{
    struct Helper
    {
        static constexpr uint64_t p2(uint8_t n)
        {
            return static_cast<uint64_t>(1) << n;
        }
    };

    using DenomArray = std::array<uint64_t, Helper::p2(5)>;
};
Run Code Online (Sandbox Code Playgroud)

使用 GCC 我得到:

error: 'static constexpr uint64_t A::Helper::p2(uint8_t)' called in a constant expression before its definition is complete
Run Code Online (Sandbox Code Playgroud)

我的理解是p2应该定义函数,因为Helper类是完全编译的。

为 x86 和 GCC 12 尝试了 MSVC 编译器版本 19.29.30040。

编辑1:

模板和非模板类的行为不同。例如下面的代码编译:

template <class T>
class A
{
private:

    struct Helper
    {
        static constexpr T p2(uint8_t n)
        {
            return static_cast<T>(1) << n;
        }
    };

    using DenomArray = std::array<T, Helper::p2(5)>;
};

using IntA = A<uint64_t>;
Run Code Online (Sandbox Code Playgroud)

mar*_*inj 2

您定义的地方DenomArray尚未A::Helper完全定义,这使得A::Helper::p2(5)表达式非常量。

有趣的是,制作A模板解决了这个问题 - 这可能是语言缺陷 - 请参阅此处:http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1626

更正式: https: //eel.is/c++draft/temp.arg.nontype#2

非类型模板参数的模板参数应为模板参数类型的转换常量表达式 ([expr.const])。

然后https://eel.is/c++draft/expr.const#5

表达式 E 是核心常量表达式,除非对 E 的求值遵循抽象机 ([intro.execution]) 的规则,将求值以下其中一项:

现在到您编码违反的部分:https ://eel.is/c++draft/expr.const#5.3

(5.3) 调用未定义的 constexpr 函数;

为什么它是未定义的?- 因为它在被视为不完整上下文的上下文中使用:

https://eel.is/c++draft/class#mem.general-7

类(模板)的完整类上下文是(7.1)函数体([dcl.fct.def.general]),(7.2)默认参数([dcl.fct.default]),(7.3)默认模板参数 ([temp.param])、(7.4) noexcept 说明符 ([ except.spec]) 或 (7.5) 默认成员初始值设定项

所以并不是说 a 的主体struct是一个完整的类上下文。你可以将你的放在using一个函数体中,它会编译:

class A
{
    struct Helper
    {
        static constexpr uint64_t p2(uint8_t n)
        {
            return static_cast<uint64_t>(1) << n;
        }
    };
   void ff() {
     using DenomArray = std::array<uint64_t, Helper::p2(5)>;
   }
};
Run Code Online (Sandbox Code Playgroud)

[编辑]另请参阅此处: https: //coliru.stacked-crooked.com/a/6536fdbe1ded1d01,clang错误与gcc不同,因为它说Helper::p2(5)是非constexpr:

错误:非类型模板参数不是常量表达式

这就是我上面解释的。