静的定数文字列(クラスメンバー)


444

クラス(この場合はシェイプファクトリ)のプライベート静的定数が欲しいのですが。

そういうのが欲しいです。

class A {
   private:
      static const string RECTANGLE = "rectangle";
}

残念ながら、C ++(g ++)コンパイラーから次のようなさまざまなエラーが出ます。

ISO C ++はメンバー「RECTANGLE」の初期化を禁止します

非整数型 'std :: string'の静的データメンバーのクラス内初期化が無効です

エラー: 'RECTANGLE'を静的にしています

これは、この種のメンバー設計が標準に準拠していないことを示しています。#defineディレクティブを使用せずにプライベートリテラル定数(またはおそらくパブリック)を使用するにはどうすればよいですか(データのグローバル化の醜さを避けたい!)

どんな助けでもありがたいです。


15
すばらしい回答をありがとうございます。ロングライブSO!
ポンド

誰かが「積分」タイプとは何か教えてもらえますか?どうもありがとうございました。
ポンド、

1
整数型は、整数を表す型を指します。publib.boulder.ibm.com/infocenter/comphelp/v8v101/…を
bleater

ファクトリのプライベート静的文字列は良い解決策ではありません-ファクトリのクライアントがサポートされている形状を知る必要があることを考慮して、プライベート静的に保持する代わりに、静的なconst std :: string RECTANGLE = "Rectangle 」
LukeCodeBaker 2016年

あなたのクラスはテンプレートクラスであるならば、参照stackoverflow.com/q/3229883/52074
トレバー・ボイド・スミス

回答:


469

クラス定義の外で静的メンバーを定義し、そこに初期化子を提供する必要があります。

最初

// 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";
};

追加の定義は必要ありません。

または、このバリアントでconst宣言することができますconstexprinlineはをconstexpr意味するため、明示は不要になりますinline


8
また、STL文字列を使用する必要がない場合は、const char *を定義することもできます。(オーバーヘッドが少ない)
KSchmidt 2009年

50
私はそれが常により少ないオーバーヘッドであるかどうかはわかりません-それは使用法に依存します。このメンバーがconst string&を受け取る関数に引数として渡されることを意図している場合、初期化中に、各呼び出しに対して1つの文字列オブジェクトの作成に対して一時的に作成されます。静的文字列オブジェクトを作成するためのIMHOオーバーヘッドは無視できます。
Tadeusz Kopec、

23
私はむしろstd :: stringをいつも使いたいと思っています。オーバーヘッドはごくわずかですが、オプションははるかに多く、 "magic" == A :: RECTANGLEのようなばかげたものを書くだけで、アドレスを比較するだけです...
Matthieu M.

9
これにchar const*は、動的な初期化がすべて完了する前に初期化されるという利点があります。そのため、どのオブジェクトのコンストラクタでも、そのRECTANGLE時点で初期化されていることが信頼できます。
ヨハネスシャウブ-litb 2009年

8
@cirosantilli:C ++の当初から、イニシャライザは宣言ではなく定義の一部であったためです。そして、クラス内のデータメンバー宣言は、宣言です。(一方で、const
integer

153

C ++ 11では、次のことができます。

class A {
 private:
  static constexpr const char* STRING = "some useful string constant";
};

30
残念ながら、このソリューションはstd :: stringでは機能しません。
HelloWorld、2015年

2
1.これはリテラルでのみ機能し、2。これは標準に準拠していませんが、Gnu / GCCは罰金を科しますが、他のコンパイラーはエラーをスローします。定義は本文にある必要があります。
ManuelSchneid3r 2015年

2
@ ManuelSchneid3rこれは、「標準に準拠していない」とは正確にどの程度ですか。私にとっては、沼地の標準のC ++ 11中括弧または初期化のように見えます。
underscore_d

3
@rvighne、それは間違いではありません。それが指すタイプではなく、varをconstexpr意味constします。つまりはstatic constexpr const char* const同じですstatic constexpr const char*が、同じではありませんstatic constexpr char*
midenok

2
@ abyss.7-あなたの答えをありがとう、そして私はもう一つお願いします:なぜそれは静的でなければならないのですか?
Guy Avraham

34

クラス定義内では、静的メンバーのみを宣言できます。これらはクラスの外で定義する必要があります。コンパイル時の積分定数の場合、標準では、メンバーを「初期化」できるという例外があります。しかし、それはまだ定義ではありません。たとえば、アドレスを取ることは、定義なしでは機能しません。

定数に const char []よりもstd :: stringを使用する利点が見当たらないことを述べたいと思います。std :: stringはすばらしいですが、動的な初期化が必要です。したがって、次のようなものを書いた場合

const std::string foo = "hello";

名前空間スコープでは、fooのコンストラクターがメインの開始の直前に実行され、このコンストラクターは定数「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。


1
これはC ++ 11以前の回答です。標準のC ++を使用し、std :: string_viewを使用します。

1
C ++ 11にはstd :: string_viewがありません。
Lukas Salich

17

これは単なる追加情報ですが、ヘッダーファイル内の文字列が本当に必要な場合は、次のようにしてください。

class foo
{
public:
    static const std::string& RECTANGLE(void)
    {
        static const std::string str = "rectangle";

        return str;
    }
};

それはお勧めしませんが。


それはクールに見えます:)-あなたはc ++以外の言語のバックグラウンドを持っていると思いますか?
ポンド

5
私はそれをお勧めしません。私はこれを頻繁に行います。それはうまく機能し、実装ファイルに文字列を置くよりもわかりやすいと思います。std :: stringの実際のデータはヒープ上にあります。私はconst char *を返します。その場合、静的変数を宣言する必要がないため、宣言に必要なスペースが少なくなります(コードに関して)。ただ好みの問題です。
Zoomulator

15

C ++ 17では、インライン変数を使用できます

class A {
 private:
  static inline const std::string my_string = "some useful string constant";
};

これはabyss.7の回答とは異なることに注意してください。これは実際のstd::stringオブジェクトを定義するものであり、const char*


使用inlineすると多くの複製が作成されると思いませんか?
shuva


8

そのクラス内初期化構文を使用するには、定数は、定数式によって初期化された整数型または列挙型の静的定数である必要があります。

これが制限です。したがって、この場合は、クラスの外部で変数を定義する必要があります。@AndreyTからの回答を参照してください


7

クラス静的変数はヘッダーで宣言できます、.cppファイルで定義する必要があります。これは、静的変数のインスタンスは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からのみ使用されることを意図しているため-あり

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 };

この場合、静的変数ヘッダーには、リンカーによって作成された初期化の順序に応じて、{""}または{".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.
}

よく準備された答えマルコ。2つの詳細:1つは静的クラスメンバーのcppファイルを必要としません。また、あらゆる種類の定数にはstd :: string_viewを使用してください。


4

可能なだけ:

static const std::string RECTANGLE() const {
    return "rectangle";
} 

または

#define RECTANGLE "rectangle"

11
型付き定数を使用できるときに#defineを使用するのは間違っています。
Artur Czajka、

最初の例は基本的にconstexprはありませんが、静的関数を作成できない場合の優れたソリューションですconst
フランクパファー2018年

このソリューションは回避する必要があります。それはすべての呼び出しで新しい文字列を作成します。これはより良いでしょう:static const std::string RECTANGLE() const { static const std::string value("rectangle"); return value; }
Oz Solomon

なぜ本格的なコンテナを戻り値として使用するのですか?std :: string_vewを使用します。この場合、コンテンツは有効なままです。文字列リテラルをさらに使用して文字列ビューを作成して返す...そして最後に、constの戻り値はここでは意味も効果もありません..ahはい、これを静的ではなくインラインとして、名前空間...そしてそれをconstexprにしてください

4

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_initializedmain()に初期化されるため、マルチスレッド環境でもロックは必要ありません。A_s_initializedデフォルトは0(動的初期化前)なので、getS()sが初期化される前にを使用すると、init関数を安全に呼び出すことができます。

ところで、上記の答え: " static const std :: string RECTANGLE()const "、静的関数はconst、オブジェクトがある場合でも状態を変更できないため(このポインターがないため)できません。


4

2018年とC ++ 17に早送りします。

  • std :: stringを使用せず、std :: string_viewリテラルを使用
  • 以下の「constexpr」に注意してください。これは「コンパイル時」のメカニズムでもあります。
  • インラインは反復を意味しない
  • これにはcppファイルは必要ありません
  • 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 ++をお楽しみください


使用std::string_viewあなたが使用している場合にのみ定数のstring_viewすべての機能にパラメータを。関数のいずれかがconst std::string&パラメーターを使用する場合、string_viewそのパラメーターを介して定数を渡すと、文字列のコピーが作成されます。定数がタイプでstd::stringある場合、コピーはconst std::string&パラメーター用にもパラメーター用にも作成されませんstd::string_view
MarkoMahnič19年

いい答えですが、なぜstring_viewが関数から返されているのか知りたいですか?この種のトリックは、inline変数がC ++ 17にODRセマンティクスとともに到着する前に役立ちました。しかしstring_viewは、あまりにもこれだけC ++ 17でconstexpr auto some_str = "compile time"sv;仕事をしていません(そして実際に、それは変数ではありません、それはですconstexprので、inline暗黙的である、あなたは、変数を持っている場合-すなわちなしconstexpr-そしてinline auto some_str = "compile time"sv;それを行います、もちろん、しかし、名前空間スコープ基本的にグローバル変数である変数は、めったに考えられません)。
喪失の心理
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.