嵌套结构体与全局结构体完全相同,但会破坏constexpr。

18

以下是我遇到问题的代码:

template<typename T>
constexpr int get(T vec) {
  return vec.get();
}

struct coord {
  constexpr int get() const { return x; }
  int x;
};

struct foo {
    struct coord2 {
      constexpr int get() const { return x; }
      int x;
    };
    constexpr static coord f = { 5 };
    constexpr static int g = get(f); // works

    constexpr static coord2 h = { 5 };
    constexpr static int i = get(h); // doesn't work
};

constexpr coord foo::f;
constexpr foo::coord2 foo::h;

int main(){}

基本上,get(f) 被认为是一个常量表达式,但 get(h) 不是。唯一变化的是一个使用全局结构体 coord,而另一个使用嵌套结构体 coord2。这两个结构体的主体都是相同的。

为什么会这样呢?


GCC 错误:

test.cpp:20:35: error: field initializer is not constant

Clang 错误:

test.cpp:20:26: error: constexpr variable 'i' must be initialized by a constant expression
    constexpr static int i = get(h); // doesn't work
                         ^   ~~~~~~
test.cpp:8:10: note: undefined function 'get' cannot be used in a constant expression
  return vec.get();
         ^
test.cpp:20:30: note: in call to 'get({5})'
    constexpr static int i = get(h); // doesn't work
                             ^
test.cpp:13:21: note: declared here
      constexpr int get() const { return x; }
1个回答

17

这是一个常量表达式...最终,你可以通过将i移入main()函数中来看到:

错误消息非常明确,说明了发生了什么,即foo::coord2::get()尚未定义,因为成员函数定义被延迟到封闭类的末尾,以便它们可以使用后面声明的成员。

有点令人惊讶的是,定义要延迟到最外层的封闭类的末尾,但如果foo::coord2::get()无法访问foo::g,你会更加惊讶。

标准与编译器意见一致。第9.2p2节的一部分说:

  

在类的成员规范中,在函数体、默认参数、异常说明和非静态数据成员的大括号或等于初始化器(包括嵌套类中的这些内容)中,该类被认为是完整的。

不幸的是,只能推断出类声明的结束括号成为这些延迟区域的定义点。我认为标准的缺陷在于它没有明确说明这一点。

另见:


@dyp:这不就是我说的吗?(这是暗示,而非明确表述) - Ben Voigt
啊,好的,我误解了那段话,抱歉。 - dyp
@Somnium:将你的初始化器移出行?如果我没记错的话,你可以为const static数据成员使用constexpr初始化器。 - Ben Voigt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接