web-dev-qa-db-ja.com

C ++で実行時に静的constメンバーを初期化できますか?

実行時にクラスの静的constメンバーを初期化することは可能ですか?この変数はプログラム全体で定数ですが、コマンドライン引数として送信したいと思います。

//A.h
class A {
public: 
    static const int T;
};

//in main method
int main(int argc,char** argv)
{
    //how can I do something like 
    A::T = atoi(argv[1]);
}

これが実行できない場合、使用する必要がある変数のタイプは何ですか?実行時に初期化し、定数プロパティを保持する必要があります。

23
user2939212

コンパイル時ではなくプログラムの起動時にstatic constシンボルを初期化することは不可能であるというコメントと回答に同意できず、申し訳ありません。

実際、これはIS可能で、何度も使用しましたが、構成ファイルから初期化しました。以下のようなものです。

// GetConfig is a function that fetches values from a configuration file
const int Param1 = GetConfig("Param1");
const int MyClass::Member1 = GetConfig("MyClass.Member1");

ご覧のとおり、これらの静的定数は必ずしもコンパイル時に認識されているわけではありません。これらは、構成ファイルなどの環境から設定できます。

一方で、それらをargv []から設定することは、可能であれば、非常に難しいように見えます。これは、main()の開始時に静的シンボルがすでに初期化されているためです。

12
A.S.H

mainの翻訳単位の静的な初期化はstaticが取得される前に行われるため、main変数の初期化はmainの開始後に生成されたデータに依存できません。制御、および他の翻訳単位での静的初期化は、main翻訳単位の静的初期化の前または後に、不特定の順序で発生する可能性があります。

ただし、次のように非表示の非const変数を初期化して、constへの参照を提供できます。

struct A {
public: 
    // Expose T as a const reference to int
    static const int& T;
};

//in main.cpp

// Make a hidden variable for the actual value
static int actualT;
// Initialize A::T to reference the hidden variable
const int& A::T(actualT);

int main(int argc,char** argv) {
    // Set the hidden variable
    actualT = atoi(argv[1]);
    // Now the publicly visible variable A::T has the correct value
    cout << A::T << endl;
}

デモ

31
dasblinkenlight

いいえ、それはできません。

これが実行できない場合、使用する変数のタイプは何ですか?

const以外のメンバーを使用できます。

class A 
{
   public: 
      static int T;
};

int A::T;

別のオプションは、Tをプライベートメンバーにし、mainをフレンドにして、値を変更できるようにし、関数を介してメンバーを公開することです。

#include <cstdlib>

class A 
{
   public: 
      static int getT() { return T; }
   private:
      static int T;
      friend int main(int argc, char** argv);
};

int A::T;

int main(int argc, char** argv)
{
   A::T = std::atoi(argv[1]);
   return 0;
}
7
R Sahu

できないだけでなく、const_castをいじってこれを試してはいけません。静的constメンバーは、読み取り専用セグメントになる可能性が非常に高く、それらを変更しようとすると、プログラムがクラッシュします。

5
SergeyA

通常、複数の構成値があります。したがって、それらを構造体に入れてください。構造体への通常のグローバルアクセスはconstです。

_const config* Config;
...
main (int argc, char* argv [])
{
Config= new config (argc, argv);
...
}
_

より洗練されたものになり、グローバルfunctionを使用して構成を返すことができるため、通常のコードではポインターを変更することもできませんが、誤って変更することは困難です。

ヘッダーファイルは、すべてのユーザーがget_config ()を公開しますが、設定方法は、そうすることを意図したコードにのみ知られています。

4
JDługosz

いいえ、変数をstaticおよびconstとして定義したため、その値を変更することはできません。その値は定義自体に設定するか、クラスAのオブジェクトを作成するときに呼び出されるコンストラクターを介して設定する必要があります。

3
NSachdeva

方法1:隠された非const変数を初期化し、constへの参照を提供します(dasblinkenlightで示されています)。

class A {
public: 
  static const int &T;
};

static int dummy = 0;
const int &A::T  = dummy;

int main() {
  dummy = 10;
  std::cout << A::T << std::endl;
}

ライブデモ

方法2:const以外の静的メンバーを使用する(R Sahuが示すように):

class A {
public: 
  static int T;
};

int A::T = 0;

int main() {
  A::T = 10;
}

ライブデモ

方法3:隠された非const変数をクラスのプライベート静的メンバーとして宣言し、それをインターフェイスする静的メンバーconst参照を提供します。フレンド関数を初期化子として定義します。

class A {
  friend void foo(int);
    static int dummy;
public: 
    static const int &T;
};

const int &A::T = A::dummy;
int A::dummy = 0;

void foo(int val) { A::dummy = val; }

int main() {
    foo(10);
    std::cout << A::T << std::endl;
}

ライブデモ

方法#4:非const隠し変数をクラスのプライベート静的メンバーとして宣言し、それをインターフェースする静的メンバーconst参照を提供します。静的メンバー関数を初期化子として定義します。

class A {
    static int dummy;
public: 
    static const int &T;
    static void foo(int val) { A::dummy = val; }
};

const int &A::T = A::dummy;
int A::dummy = 0;

int main() {
    A::foo(10);
    std::cout << A::T << std::endl;
}

ライブデモ

ボーナス:

一度だけ初期化したい場合は、ヘルパー関数を次のように変更できます。

static void foo(int val) { 
  static bool init = true;
  if(init) A::dummy = val;
  init = false;
}

ライブデモ

3
101010

最近私も同じ問題に直面していたので、@ A.S.Hの答えが完全に最も近いことがわかりましたが、変数を初期化する必要があるため、いくつかの問題が発生する可能性があります。

  • 質問どおり、argcargvなど、まだ利用できないデータソースは使用できません。
  • 一部の依存関係はまだ初期化されていない可能性があります。たとえば、多くのGUIフレームワークでは、まだ早い段階でテキストボックスを作成できません。これは問題です。構成ファイルのロードがユーザーへの通知に失敗した場合、エラーテキストボックスを表示したい場合があるためです。

だから私は次のことを思いつきました:

template <class T>
class StaticConfig
{
public:

    StaticConfig()
    {
        if (!mIsInitialised)
        {
            throw std::runtime_error("Tried to construct uninitialised StaticConfig!");
        }
    }

    const T*
    operator -> () const
    {
        return &mConfig;
    }

private:

    friend class ConfigHandler;

    StaticConfig(const T& config)
    {
        mConfig = config;
        mIsInitialised = true;
    }

    static T mConfig;
    static bool mIsInitialised;
};

template <class T>
T StaticConfig<T>::mConfig;
template <class T>
bool StaticConfig<T>::mIsInitialised = false;

データは静的ですが、constではないので、すぐに初期化する必要はなく、より適切なタイミングで正しい値を割り当てることができます。読み取り専用アクセスは、operator ->のオーバーロードを通じて与えられます。デフォルトのコンストラクターは、このタイプのStaticConfigに有効なデータがすでにロードされているかどうかをチェックし、ロードされていない場合はスローします。これは実際には発生しないはずですが、デバッグの補助として役立ちます。プライベートコンストラクターを使用すると、有効なデータで型をロードできます。データのロードを担当するConfigHandlerクラスがフレンドになり、プライベートコンストラクターにアクセスできるようになります。

ConfigHandlerインスタンスは、すべてのStaticConfigタイプを初期化するためにすべての依存関係が利用できる適切なタイミングで簡単に作成できます。完了したら、ConfigHandlerインスタンスを破棄できます。その後、クラスは単純に適切なタイプのStaticConfigをメンバーとして含め、最小限の侵入でデータの読み取り専用アクセスを行うことができます。

オンラインデモ

1
Unimportant

トリックがありますが、おそらくそれを避けるべきです!原理を説明するための最低限の例を以下に示します。

int const& foo(int i) {
    static const int j = (i == 0 ? throw 0 : i); 
    return j; 
}

int main() {
    try { 
        int x = foo(0); // oops, we throw        
    } catch(...) {}

    int x = foo(1); // initialized..
    int y = foo(0); // still works..     
}

気をつけて!

0
hs_

N-O

必要なもののセマンティクスはすべて間違っています。そのためにstatic-constを使用しないでください。

staticは、静的ストレージ期間と内部リンケージを持つオブジェクトまたは整数型です。

A const is an object that does not change its value throughout application's lifetime, any attempt to change it results in UD . ( the overwhelming majority of such cases is a pretty well defined crash )

この質問の結果として、暗黙の動作を模倣するために危険な回避策が提案されました。ほとんどの例では、static-const-referenceには、実行時に割り当て可能な何らかの方法で隠されたstaticが与えられます。 これ

このようなコードを維持することの難しさは別として、宣言されたセマンティクスが実際に強制されないという問題が残っています。

たとえば、アプリケーションランタイム全体で値constを維持する場合、完全に有効なconst_cast<int &>(A::T) = 42を実行することでハッキングできます。参照される型はconstではないため、コードを完全に定義します。

ここで求められているものは、アプリケーション全体で一度だけ初期化できるクラスで、内部リンケージがあり、アプリケーションの存続期間があります。

だからそれを行うテンプレートクラスを実行してください:

template<typename V> class fixation
{
  bool init = true;
 V val;
  public:

    fixation(V const & v) : init(true), val(v) {}

    fixation & operator=( fixation arg)
    {
      if(init )
      {
        this->val = arg.val;
      }
      this->init = false;
      return *this;
    }


    V get()
    {
      return val;
    }
};

struct A
{
    static fixation<int> T;
};

2回目に呼び出された場合の処理​​方法、つまり実装の決定。この例では、値は完全に無視されます。例外をスローしたり、アサーションを実行したりすることもできます。

0
g24l