ヘッダーファイルで静的const char *を宣言する方法は?
質問
使用する.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が整数型でのみ許可される理由に関するOPの質問に答えるため。
オブジェクトが左辺値(つまり、ストレージ内にアドレスを持つもの)として使用される場合、「1つの定義ルール」を満たす必要があります。 (ODR)、つまり、1つだけの翻訳単位で定義する必要があります。コンパイラは、そのオブジェクトを定義する翻訳単位を決定することはできず、決定しません。これはあなたの責任です。オブジェクトを定義するだけではなく、どこかで定義することで、実際にはコンパイラに this 固有の here で定義したいことを伝えています。翻訳単位。
一方、C ++言語では、整数定数には特別なステータスがあります。整数定数式(ICE)を形成できます。 ICEでは、整数定数はオブジェクトではなく通常の値として使用されます(つまり、そのような整数値がストレージ内にアドレスを持っているかどうかは関係ありません)。実際、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&amp;&amp; ./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
と考えてください。そうすれば、型の一部としてではなく、オブジェクト。