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

Foo::fここで、g++は。の違いの再宣言について文句を言いconstexprます。

4

4 に答える 4

36

私が規格を正しく解釈した場合、それは不可能です。

(§9.4.2/ 3)[...]リテラル型の静的データメンバーは、constexpr指定子を使用してクラス定義で宣言できます。その場合、その宣言は、代入式であるすべての初期化子句が定数式である中括弧または等しい初期化子を指定するものとします。[...]

上記から(静的データメンバー宣言に非リテラル型に関する個別のステートメントがないという事実とともに)、(§3.9/ 10で定義されているように)リテラル型constexprでなければならない静的データメンバーが続くと私は信じています)、およびその定義が宣言に含まれている必要があります。後者の条件は、次のコードを使用して満たすことができます。

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

これは、試行1に似ていますが、クラス外部の定義がありません。

ただし、Foo静的メンバーの宣言/定義時には不完全であるため、コンパイラーはそれがリテラル型(§3.9/ 10で定義されている)であるかどうかを確認できず、コードを拒否します。

このC++-11以降のドキュメント(N3308)constexprには、標準での現在の定義のさまざまな問題について説明し、修正の提案を行っていることに注意してください。具体的には、「提案された文言」セクションは、リテラル型の1つの種類として不完全な型を含めることを意味する§3.9/10の修正を提案しています。その修正が標準の将来のバージョンに受け入れられるとしたら、あなたの問題は解決されるでしょう。

于 2012-08-13T07:04:45.683 に答える
18

GCCがあなたの試み3を拒否するのは間違っていると思います。C++11標準(またはその受け入れられた欠陥レポート)には、変数の再宣言はconstexpr前の宣言とは異なる必要があるという規則はありません。そのルールに最も近い標準は[dcl.constexpr](7.1.5)/ 1_にあります:

関数または関数テンプレートの宣言に指定子がある場合、そのすべての宣言には指定子constexprが含まれている必要があります。constexpr

Clangの実装constexprはあなたの試み3を受け入れます。

于 2012-09-24T03:52:20.497 に答える
13

リチャード・スミスの答えの更新である試行3は、GCC 4.9と5.1の両方、およびclang3.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は失敗しますが、GCC4.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

以前、私は同じ問題を抱えていて、この10年前の質問に出くわしました。その間に解決策が現れたことを報告できてうれしいです。上記の「試行3」のようなことをする必要がありますが、の定義をFoo::fとしてマークします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"

main.cpp

#include "foo.h"

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