Can I initialize a static const member at run-time

2019-01-18 10:29发布

问题:

Is it possible to initialize a static const member of my class during run-time? This variable is a constant throughout my program but I want to send it as a command-line argument.

//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]);
}

If this cannot be done, what is the type of variable I should use? I need to initialize it at run-time as well as preserve the constant property.

回答1:

I am sorry to disagree with the comments and answers saying that it is not possible for a static const symbol to be initialized at program startup rather than at compile time.

Actually this IS possible, and I used it many times, BUT I initialize it from a configuration file. Something like:

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

As you see, these static consts are not necessarily known at compile time. They can be set from the environment, such as a config file.

On the other hand, setting them from argv[], seems very difficult, if ever feasible, because when main() starts, static symbols are already initialized.



回答2:

You cannot rely on data produced after your main has started for initialization of static variables, because static initialization in the translation unit of main happens before main gets control, and static initialization in other translation units may happen before or after static initialization of main translation unit in unspecified order.

However, you can initialize a hidden non-const variable, and provide a const reference to it, like this:

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

Demo.



回答3:

No, you cannot do that.

If this cannot be done what is the type of variable I should use ?

You can use a non-const member.

class A 
{
   public: 
      static int T;
};

int A::T;

Another option is to make T a private member, make main a friend so only it can modify the value, and then expose the member through a function.

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


回答4:

Not only you can't, you should not try doing this by messing with const_cast. Static const members have a very high chance of ending up in read-only segment, and any attempt to modify them will cause program to crash.



回答5:

Typically you will have more than one configuration value. So put them in a struct, and the normal global access to it is const.

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

You can get fancier and have a global function to return config, so normal code can't even change the pointer, but it is harder to do that by accident.

A header file exposes get_config () for all to use, but the way to set it is only known to the code that's meant to do so.



回答6:

No, since you defined the variable as static and const, you cannot change its value. You will have to set its value in the definition itself, or through a constructor called when you create an object of class A.



回答7:

Method #1: Initialize a hidden non-const variable, and provide a const reference to it (as shown by 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;
}

Live Demo

Method #2: Use a non const static member (as shown by R Sahu):

class A {
public: 
  static int T;
};

int A::T = 0;

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

Live Demo

Method #3: Declare a hidden non-const variable as a private static member of your class and provide a static member const reference to interface it. Define a friend function as inititalizer:

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

Live Demo

Method #4: Declare a hidden non-const variable as a private static member of your class and provide a static member const reference to interface it. Define a static member function as inititalizer:

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

Live Demo

Bonus:

If you want to initialize only once you can change the helper function to:

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

Live Demo



回答8:

N - O

The semantics of what is being required are all wrong, and you shouldn't use a static-const for that.

A static is an object or integral type which has static storage duration and internal linkage.

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 )

As a result of this question dangerous workarounds have been proposed to mimic the implied behavior. In most of examples a static-const-reference is given a somehow hidden static which is assignable at runtime, e.g. this.

Apart from the difficulties in maintaining such code, the problem remains that declared semantics are not actually enforced.

For example in keeping the value const throughout the application runtime can be hacked by doing const_cast<int &>(A::T) = 42 , which is perfectly valid, perfectly define code since the referenced type is not const.

What is being sought after here is an class that permits to be initialized only once throughout the application, has internal linkage, and the lifetime of the application.

So just do a template class that does that:

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

How to handle the case that it is called a second time, that is an implementation decision. In this example the value is totally ignored. Others may prefer to throw an exception, do an assertion, ... etc.



回答9:

There is a trick, but you should probably avoid it! Here's a bare bones example to illustrate the principle:

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

Careful!



回答10:

Use a Singleton Pattern here. have a data member which you'd like to initialize at run time in the singleton class. One a single instance is created and the data member is properly initialized, there would be no further risk of overwriting it and altering it.

Singleton would preserve the singularity of your data.

Hope this helps.