クラス(この場合はシェイプファクトリ)にプライベートな静的定数が欲しいのですが。
そのようなものが欲しいのですが。
class A {
private:
static const string RECTANGLE = "rectangle";
}
残念ながら、私はC++(g ++)コンパイラから以下のようなあらゆる種類のエラーを受け取ります。
ISO C++は、メンバー「RECTANGLE」の初期化を禁止しています
非整数型 'std :: string'の静的データメンバのクラス内初期化が無効です
エラー:「RECTANGLE」を静的にする
これは、この種の部材設計が規格に準拠していないことを示しています。 #defineディレクティブを使用しなくても、プライベートリテラル定数(またはおそらくパブリック)をどのように持つことができますか(データグローバルの醜さを避けたいのです)。
任意の助けは大歓迎です。
静的メンバーをクラス定義の外側で定義し、そこに初期化子を提供する必要があります。
最初
// In a header file (if it is in a header file in your case)
class A {
private:
static const string RECTANGLE;
};
その後
// In one of the implementation files
const string A::RECTANGLE = "rectangle";
元々使用しようとしていた構文(クラス定義内の初期化子)は、整数型と列挙型でのみ許可されています。
C++ 17以降、他のオプションがあります。これは、元の宣言と非常に似ています。インライン変数
// In a header file (if it is in a header file in your case)
class A {
private:
inline static const string RECTANGLE = "rectangle";
};
追加の定義は必要ありません。
C++ 11では、次のことができるようになりました。
class A {
private:
static constexpr const char* STRING = "some useful string constant";
};
クラス定義内では、 宣言できるのは staticメンバだけです。それらはクラスの外側で defined である必要があります。コンパイル時の整数定数の場合、標準ではメンバを「初期化」できるという例外があります。それはまだ定義ではありません。たとえば、住所を入力しても定義がないとうまくいきません。
Const char [] の代わりにstd :: stringを使用した場合の利点は、定数 には見られません。 std :: stringはいいものですが、動的な初期化が必要です。ですから、あなたがこんな風に書いたら
const std::string foo = "hello";
名前空間スコープでは、fooのコンストラクタはmainの実行直前に実行され、このコンストラクタは定数 "hello"のコピーをヒープメモリに作成します。あなたが本当にRECTANGLEがstd :: stringであることを本当に必要としない限り、あなたは書くことができます
// class definition with incomplete static member could be in a header file
class A {
static const char RECTANGLE[];
};
// this needs to be placed in a single translation unit only
const char A::RECTANGLE[] = "rectangle";
そこ!ヒープ割り当て、コピー、動的初期化はありません。
乾杯、s。
これは単なる追加情報ですが、ヘッダーファイルに文字列が本当に必要な場合は、次のようにしてください。
class foo
{
public:
static const std::string& RECTANGLE(void)
{
static const std::string str = "rectangle";
return str;
}
};
それはお勧めできませんが。
C++ 17では、 インライン変数 を使用できます。
class A {
private:
static inline const std::string my_string = "some useful string constant";
};
これは abyss.7's answer とは異なることに注意してください。これは実際のstd::string
オブジェクトを定義するもので、const char*
を定義するものではありません。
このクラス内初期化構文を使用するには、定数は、定数式で初期化された整数型または列挙型の静的定数である必要があります。
これが制限です。したがって、この場合は、クラス外で変数を定義する必要があります。 @AndreyTからの回答者を参照
あなたは上記のconst char*
解決策のどちらかに行くことができます、しかしもしあなたが常に文字列を必要とするなら、あなたは多くのオーバーヘッドを持つことになるでしょう。
一方、静的文字列は動的な初期化が必要なので、別のグローバル/静的変数の初期化中にその値を使用したい場合、初期化順序の問題にぶつかるかもしれません。それを避けるために、最も安いのは、あなたのオブジェクトが初期化されているかどうかをチェックするゲッターを通して静的な文字列オブジェクトにアクセスすることです。
//in a header
class A{
static string s;
public:
static string getS();
};
//in implementation
string A::s;
namespace{
bool init_A_s(){
A::s = string("foo");
return true;
}
bool A_s_initialized = init_A_s();
}
string A::getS(){
if (!A_s_initialized)
A_s_initialized = init_A_s();
return s;
}
A::getS()
のみを使用するのを忘れないでください。スレッド化はmain()
でしか開始できず、A_s_initialized
はmain()
より前に初期化されるため、マルチスレッド環境でもロックは必要ありません。 A_s_initialized
はデフォルト(動的初期化前)では0です。そのため、sが初期化される前にgetS()
を使用する場合は、init関数を安全に呼び出します。
ところで、上記の答えでは " static const std :: string RECTANGLE()const "ですが、静的関数はconst
にはできません。
現在の規格では、静的定数整数型に対してのみそのような初期化を許可しています。だからAndreyTが説明したようにする必要があります。しかしながら、それは 新しいメンバ初期化構文 を通して次の規格で利用可能になるでしょう。
クラス静的変数は、ヘッダー内で 宣言された にすることができますが、.cppファイル内では defined にする必要があります。これは、静的変数のインスタンスは1つしか存在できず、コンパイラがどの生成済みオブジェクトファイルに配置するかを決定できないため、代わりに決定する必要があるためです。
C++ 11の宣言で静的値の定義を維持するために、ネストした静的構造を使用することができます。この場合、静的メンバーは構造体であり、.cppファイルで定義する必要がありますが、値はヘッダーにあります。
class A
{
private:
static struct _Shapes {
const std::string RECTANGLE {"rectangle"};
const std::string CIRCLE {"circle"};
} shape;
};
個々のメンバを初期化する代わりに、静的構造全体が.cppで初期化されます。
A::_Shapes A::shape;
値は次のようにしてアクセスされます。
A::shape.RECTANGLE;
または - メンバーは非公開で、Aからのみ使用されることを意図しているため、 - with
shape.RECTANGLE;
この解決法は依然として静的変数の初期化の順序の問題を抱えていることに留意されたい。静的値を使用して別の静的変数を初期化する場合、最初のものはまだ初期化されていない可能性があります。
// file.h
class File {
public:
static struct _Extensions {
const std::string h{ ".h" };
const std::string hpp{ ".hpp" };
const std::string c{ ".c" };
const std::string cpp{ ".cpp" };
} extension;
};
// file.cpp
File::_Extensions File::extension;
// module.cpp
static std::set<std::string> headers{ File::extension.h, File::extension.hpp };
この場合、静的変数 headers には、リンカによって作成された初期化の順序に応じて、{""}または{".h"、 ".hpp"}のいずれかが含まれます。
@ abyss.7で述べたように、変数の値がコンパイル時に計算できるのであればconstexpr
を使うこともできます。しかし、あなたがstatic constexpr const char*
であなたの文字列を宣言し、そしてあなたのプログラムがstd::string
を使うなら、あなたがそのような定数を使うたびに新しいstd::string
オブジェクトが作られるのでオーバーヘッドがあるでしょう:
class A {
public:
static constexpr const char* STRING = "some value";
};
void foo(const std::string& bar);
int main() {
foo(A::STRING); // a new std::string is constructed and destroyed.
}
可能なだけです:
static const std::string RECTANGLE() const {
return "rectangle";
}
または
#define RECTANGLE "rectangle"
2018年とC++ 17に早送りします。
static_assertはコンパイル時にのみ動作します。
using namespace std::literals;
namespace STANDARD {
constexpr
inline
auto
compiletime_static_string_view_constant() {
// make and return string view literal
// will stay the same for the whole application lifetime
// will exhibit standard and expected interface
// will be usable at both
// runtime and compile time
// by value semantics implemented for you
auto when_needed_ = "compile time"sv;
return when_needed_ ;
}
;
上記は適切で合法的な標準C++市民です。それは、ありとあらゆるstd ::アルゴリズム、コンテナ、ユーティリティなどに容易に関わることができます。例えば:
// test the resilience
auto return_by_val = []() {
auto return_by_val = []() {
auto return_by_val = []() {
auto return_by_val = []() {
return STANDARD::compiletime_static_string_view_constant();
};
return return_by_val();
};
return return_by_val();
};
return return_by_val();
};
// actually a run time
_ASSERTE(return_by_val() == "compile time");
// compile time
static_assert(
STANDARD::compiletime_static_string_view_constant()
== "compile time"
);
標準のC++を楽しむ