为什么下面的代码不能编译?
#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:
错误:非类型模板参数不是常量表达式
就是我上面解释过的。