constexpr 函数调用无法编译



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

#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)>;
};

使用GCC我得到:

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

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

尝试MSVC编译器版本19.29.30040用于x86和GCC 12.

EDIT1:

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

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>;

您定义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 + +/草案expr.const # 5:

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

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

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

为什么没有定义?-因为它在一个被认为是不完整的上下文中使用:

https://eel.is/c + +/类#草案mem.general-7

类(模板)的完整类上下文是(7.1)函数体([dcl.fct.def.general]),(7.2)默认参数([dcl.fct.default]),(7.3)默认模板参数([temp.param]),(7.4) noexcept-specifier ([exception .spec]),或(7.5)default成员初始化器

所以并不是说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)>;
}
};

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

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

就是我上面解释过的。

最新更新