使用する.cppファイルのヘッダーファイルに定数char *を定義したいと思います。だから私はこれを試しました:
private:
static const char *SOMETHING = "sommething";
次のコンパイラエラーが発生します。
エラーC2864: 'SomeClass :: SOMETHING':クラス内で静的なconst整数データメンバーのみを初期化できます
私はC++が初めてです。ここで何が起こっていますか?なぜこれが違法なのですか?そして、どうすれば代わりにそれを行うことができますか?
整数型でない限り、翻訳単位で静的変数を定義する必要があります。
ヘッダー内:
private:
static const char *SOMETHING;
static const int MyInt = 8; // would be ok
.cppファイル内:
const char *YourClass::SOMETHING = "something";
C++標準、9.4.2/4:
静的データメンバーがconst整数型またはconst列挙型である場合、クラス定義でのその宣言は、整数定数式となる定数初期化子を指定できます。その場合、メンバーはそのスコープ内の整数定数式で表示できます。メンバーは、プログラムで使用される場合、名前空間スコープで定義され、名前空間スコープ定義には初期化子が含まれないものとします。
なぜ整数型でのみ許可されるのかについてのOPの質問に答えるため。
オブジェクトが左辺値(つまり、ストレージ内にアドレスを持つもの)として使用される場合、「1つの定義ルール」(ODR)を満たす必要があります。つまり、1つだけの変換単位で定義する必要があります。コンパイラは、そのオブジェクトを定義する翻訳単位を決定することはできませんし、決定しません。これはあなたの責任です。 定義そのオブジェクトを定義するだけではなく、そのオブジェクトを定義することをコンパイラーに実際に伝えていますhere、in this specific翻訳単位。
一方、C++言語では、整数定数には特別なステータスがあります。整数定数式(ICE)を形成できます。 ICEでは、整数定数は通常のvaluesとして使用され、objectsとしてではありません(つまり、そのような整数値がストレージにアドレスを持っているかどうかは関係ありません)。実際、ICEはコンパイル時に評価されます。整数定数のこのような使用を容易にするために、それらの値はグローバルに見える必要があります。そして、定数自体は実際にストレージ内に実際の場所を必要としません。このため、整数定数は特別な扱いを受けました。ヘッダーファイルにイニシャライザを含めることが許可され、定義を提供する要件が緩和されました(最初は事実上、次に法律)。
他の定数タイプにはそのようなプロパティはありません。他の定数タイプは、実質的に常に左辺値として使用されます(または少なくともICEまたはICEに類似したものには参加できません)。つまり、定義が必要です。残りは続きます。
エラーは、クラス内でstatic const char*
を初期化できないことです。整数変数のみを初期化できます。
クラスでメンバー変数を宣言し、クラス外で初期化する必要があります。
//ヘッダーファイル
class Foo {
static const char *SOMETHING;
// rest of class
};
// cppファイル
const char *Foo::SOMETHING = "sommething";
これが迷惑に思える場合は、初期化は1つの翻訳単位にしか表示されないため、そのように考えてください。クラス定義にある場合、通常は複数のファイルに含まれます。定数整数は特殊なケースであり(エラーメッセージはおそらく明確ではないことを意味します)、コンパイラーは効果的に変数の使用を整数値に置き換えることができます。
対照的に、char*
変数は、実際に存在するために必要なメモリ内の実際のオブジェクトを指し、オブジェクトを存在させるのは定義(初期化を含む)です。 「1つの定義ルール」とは、そのヘッダーを含むすべての翻訳単位に定義が含まれるため、ヘッダーに配置しないことを意味します。現在のC++ルールでは、同じ名前の2つの異なるオブジェクトを定義しているため、文字列の両方に同じ文字が含まれていても、それらを一緒にリンクすることはできません。彼らが偶然同じ文字を持っているという事実はそれを合法にしません。
C++ 11では、constexpr
キーワードを使用してヘッダーに書き込むことができます。
private:
static constexpr const char* SOMETHING = "something";
注:
constexpr
はSOMETHING
を定数ポインターにするため、記述できません
SOMETHING = "something different";
後で。
コンパイラによっては、.cppファイルに明示的な定義を記述する必要がある場合もあります。
constexpr const char* MyClass::SOMETHING;
class A{
public:
static const char* SOMETHING() { return "something"; }
};
特に高価なconstのデフォルトパラメータの場合は、常にそれを行います。
class A{
static
const expensive_to_construct&
default_expensive_to_construct(){
static const expensive_to_construct xp2c(whatever is needed);
return xp2c;
}
};
Visual C++を使用している場合、リンカーへのヒントを使用して移植性のない方法でこれを行うことができます...
_// In foo.h...
class Foo
{
public:
static const char *Bar;
};
// Still in foo.h; doesn't need to be in a .cpp file...
__declspec(selectany)
const char *Foo::Bar = "Blah";
_
__declspec(selectany)
は、_Foo::Bar
_が複数のオブジェクトファイルで宣言されても、リンカーは1つだけを選択することを意味します。
これは、Microsoftツールチェーンでのみ機能することに注意してください。これが移植可能であると期待しないでください。
Hファイルのみの定数を提供するためにテンプレートで使用できるトリックがあります。
(注意、これは見苦しい例ですが、少なくともg ++ 4.6.1ではそのまま動作します。)
(values.hppファイル)
#include <string>
template<int dummy>
class tValues
{
public:
static const char* myValue;
};
template <int dummy> const char* tValues<dummy>::myValue = "This is a value";
typedef tValues<0> Values;
std::string otherCompUnit(); // test from other compilation unit
(main.cpp)
#include <iostream>
#include "values.hpp"
int main()
{
std::cout << "from main: " << Values::myValue << std::endl;
std::cout << "from other: " << otherCompUnit() << std::endl;
}
(other.cpp)
#include "values.hpp"
std::string otherCompUnit () {
return std::string(Values::myValue);
}
コンパイルし(g ++ -o main main.cpp other.cpp && ./main)、ヘッダーで宣言された同じ定数を参照する2つのコンパイル単位を確認します。
from main: This is a value
from other: This is a value
MSVCでは、代わりに__declspec(selectany)を使用できる場合があります
例えば:
__declspec(selectany) const char* data = "My data";
整数型または列挙型に対してのみC++標準で許可される定数初期化子。詳細については、9.4.2/4を参照してください。
静的データメンバがconst整数型またはconst列挙型の場合、クラス定義での宣言は、整数定数式(5.19)となる定数初期化子を指定できます。その場合、メンバーは整数定数式で表示できます。メンバーは、プログラムで使用され、名前空間スコープ定義に初期化子が含まれていない場合、名前空間スコープで定義されます。
そして9.4.2/7:
静的データメンバは、非ローカルオブジェクトとまったく同じように初期化および破棄されます(3.6.2、3.6.3)。
したがって、cppファイルのどこかに書き込む必要があります。
const char* SomeClass::SOMETHING = "sommething";
whyの質問に答えるために、整数型は、割り当てられたオブジェクトへの参照ではなく、複製およびコピーされる値であるという点で特別です。これは、言語が定義されたときに行われた実装の決定にすぎません。これは、オブジェクトシステムの外部で、可能な限り効率的かつ「インライン」に値を処理することでした。
これは、それらが型の初期化子として許可される理由を正確には説明しませんが、本質的に#define
そして、それはオブジェクトの一部ではなく、タイプの一部として意味をなします。