48

我希望 C 类有一个 C 类型的静态 constexpr 成员。这在 C++11 中可能吗?

尝试1:

struct Foo {
    constexpr Foo() {}
    static constexpr Foo f = Foo();
};
constexpr Foo Foo::f;

g++ 4.7.0 说:“不完整类型的无效使用”指的是Foo()调用。

尝试2:

struct Foo {
    constexpr Foo() {}
    static constexpr Foo f;
};
constexpr Foo Foo::f = Foo();

现在的问题是类定义中的constexpr成员缺少初始化程序。f

尝试 3:

struct Foo {
    constexpr Foo() {}
    static const Foo f;
};
constexpr Foo Foo::f = Foo();

现在 g++ 抱怨重新声明Foo::fconstexpr.

4

4 回答 4

36

如果我正确解释标准,这是不可能的。

(§9.4.2/3) [...] 可以使用 constexpr 说明符在类定义中声明文字类型的静态数据成员;如果是这样,它的声明应指定一个大括号或等式初始化器,其中作为赋值表达式的每个初始化器子句都是一个常量表达式。[...]

从上面(以及在静态数据成员声明中没有关于非文字类型的单独声明这一事实),我相信它constexpr必须是文字类型的静态数据成员(如 §3.9/10 中所定义) ),并且它的定义必须包含在声明中。后一个条件可以通过使用以下代码来满足:

struct Foo {
  constexpr Foo() {}
  static constexpr Foo f {};
};

这类似于您的尝试 1,但没有类外部定义。

但是,由于Foo在静态成员的声明/定义时是不完整的,编译器无法检查它是否是文字类型(如 §3.9/10 中所定义),因此它拒绝该代码。

请注意,有这个 C++-11 后文档 (N3308)讨论了标准中当前定义的各种问题constexpr,并提出了修改建议。具体来说,“提议的措辞”部分建议对 §3.9/10 进行修订,暗示将不完整类型作为一种文字类型包含在内。如果该修订被接受到标准的未来版本中,您的问题将得到解决。

于 2012-08-13T07:04:45.683 回答
18

我认为 GCC 拒绝您的尝试 3 是不正确的。C++11 标准(或其任何已接受的缺陷报告)中没有规定重新声明变量必须是constexpriff 先前的声明。最接近该规则的标准是[dcl.constexpr](7.1.5)/1_

如果函数或函数模板的任何声明具有说明constexpr符,则其所有声明都应包含说明constexpr符。

Clang 的实现constexpr接受您的尝试 3。

于 2012-09-24T03:52:20.497 回答
13

更新Richard Smith 的答案,尝试 3 现在可以在 GCC 4.9 和 5.1 以及 clang 3.4 上编译。

struct Foo {
  std::size_t v;
  constexpr Foo() : v(){}
  static const Foo f;
};

constexpr const Foo Foo::f = Foo();

std::array<int, Foo::f.v> a;

但是,当 Foo 是类模板时,clang 3.4 失败,但 GCC 4.9 和 5.1 仍然可以正常工作:

template < class T >
struct Foo {
  T v;
  constexpr Foo() : v(){}
  static const Foo f;
};

template < class T >
constexpr const Foo<T> Foo<T>::f = Foo();

std::array<int, Foo<std::size_t>::f.v> a; // gcc ok, clang complains

叮当错误:

error: non-type template argument is not a constant expression
std::array<int, Foo<std::size_t>::f.v> a;
                ^~~~~~~~~~~~~~~~~~~~~
于 2015-08-21T07:32:18.100 回答
1

早些时候我遇到了同样的问题,遇到了这个十年前的问题。我很高兴地报告说,在这几年中出现了解决方案。我们只需要执行上面的“尝试 3”之类的操作,但将定义标记为Foo::fas inline。编译的最小示例g++ --std=c++17

foo.hpp

#ifndef __FOO_HPP
#define __FOO_HPP

struct Foo
{
    constexpr Foo() {}
    static const Foo f;
};

inline constexpr Foo Foo::f = Foo();

#endif

foo.cpp

#include "foo.h"

主文件

#include "foo.h"

int main(int, char **) { return 0; }
于 2021-09-10T18:35:31.650 回答