Dinesh Maurya
Dinesh Maurya

Reputation: 852

Initialization of map from static data member of other classes

We have classes which have exposed the static data member as public members like

class A{
public:
    static const string NAME;
    //Other class specific methods
};

And similarly class B and few other classes.

If I have to create a map something like

static const map<string, string> versionMap = {{A().NAME, "Aa"},
                                               {B().NAME, "Bb"}
                                              };

Temporary objects of classes being created while creating the versionMap, is it guaranteed to have defined behavior all the time?

Upvotes: 2

Views: 404

Answers (3)

6502
6502

Reputation: 114481

It is guaranteed to work only if

  • the map is defined in the SAME COMPILATION UNIT where the static member is defined (e.g. A.cpp)
  • the map is defined AFTER the definition of the static data member

You're however playing with fire... instead of depending on the static initialization order it's probably better to use a local static variable defined inside a static member function; i.e.

// File A.h

struct A {
    static const std::string& NAME();
};

// File Map.cpp

std::map<std::string, std::string> x{{A::NAME(), "A"}};

// File A.cpp

static const std::string& A::NAME() {
    static std::string x = "A string";
    return x;
}

This approach is guaranteed to work because local static variables are initialized at the first use (and even automatically protected for multithreading issues in C++11).

Upvotes: 1

einpoklum
einpoklum

Reputation: 131546

It seems like you might be implementing some sort of reflection mechanism, with class names. Why not actually get class names, as names?

Have a look at:

Can I obtain C++ type names in a constexpr way?

the accepted answer there allows you to write get_name<A>() and get a string(_view) which is "A". And get_name<B>() will be "B" and so on. This is might be useful in your case, since:

  1. No dynamically-allocated data.
  2. No construction of any objects (although @jhill515 also gives you that)
  3. No need for static members - this can work with classes that you don't even control!

Upvotes: 1

jhill515
jhill515

Reputation: 943

When you define a static member of a class, regardless of whether or not that class is instantiated, that member is accessible through memory. In this case, since they're public members, using the strings themselves as keys is perfectly valid.

That said, your static constant members need to be initialized after the class definition, typically something like:

class Foo { static const int foo; };
const int Foo::foo = 42;

For your static map example, you need to keep in mind that the map must be initialized similarly if used as a class member. Here's a working example:

// Compiled with g++ --std=c++17 -Wall -Wextra -Werror ConstStaticMapExample.cpp -o ConstStaticMapExample
#include <iostream>
#include <string>
#include <map>

class A
{
public:
  static const std::string NAME;
  //Other class specific methods
};
const std::string A::NAME = "foo";

class B
{
public:
  static const std::string NAME;
  //Other class specific methods
};
const std::string B::NAME = "bar";

class C
{
public:
  static const std::map<std::string, std::string> versionMap;
  // More definitions
};
const std::map<std::string, std::string> C::versionMap = {{A::NAME,"aA"},{B::NAME,"bB"}}; // Reversed for explanation

int main(int,char**)
{
  // local static
  static const std::map<std::string, std::string> versionMap = {{A::NAME,"Aa"},{B::NAME,"Bb"}};
  std::cout << "LOCAL STATIC EXAMPLE:" << std::endl;
  for(auto mpair : versionMap)
  {
    std::cout << "Key: " << mpair.first << "\tVal: " << mpair.second << std::endl;
  }

  // class member static
  std::cout << "CLASS MEMBER STATIC EXAMPLE:" << std::endl;
  for(auto mpair : C::versionMap)
  {
    std::cout << "Key: " << mpair.first << "\tVal: " << mpair.second << std::endl;
  }

  return 0;
}

Gist

Upvotes: 0

Related Questions