C++, can I statically initialize a std::map at compile time?

╄→гoц情女王★ 提交于 2019-11-27 08:12:34
Artyom

Not in C++98. C++11 supports this, so if you enable C++11 flags and include what g++ suggests, you can.

Edit: from gcc 5 C++11 is on by default

It's not exactly static initialization, but still, give it a try. If your compiler doesn't support C++0x, I'd go for std::map's iteration constructor:

std::pair<int, std::string> map_data[] = {
    std::make_pair(1, "a"),
    std::make_pair(2, "b"),
    std::make_pair(3, "c")
};

std::map<int, std::string> my_map(map_data,
    map_data + sizeof map_data / sizeof map_data[0]);

This is pretty readable, doesn't require any extra libraries and should work in all compilers.

You can use Boost.Assign library:

#include <boost/assign.hpp>
#include <map>
int main()
{
   std::map<int, char> example = 
      boost::assign::map_list_of(1, 'a') (2, 'b') (3, 'c');
}

However, as Neil and others pointed in the comments below, this initialization occurs in runtime, similarly to UncleBean's proposal.

With C++0x you might need to use braces all the way (use the new-style syntax for each pair as well):

std::map<int, char> example = { {1,'a'}, {2, 'b'}, {3, 'c'} };

Those brackets to construct pairs are not meaningful. Alternatively you can fully name out each pair or use make_pair (as you'd do in C++98)

std::map<int, char> example = {
    std::make_pair(1,'a'),
    std::make_pair(2, 'b'),
    std::make_pair(3, 'c')
};

As to creating those instances at compile-time: no. STL containers all encapsulate entirely runtime memory management.

I suppose, you'd only really have a compile-time map with libraries like boost's metaprogramming (not 100% sure, if it is entirely correct, and haven't studied what it could be good for):

using namespace boost::mpl;
map<
    pair<integral_c<int, 1>, integral_c<char, 'a'> >,
    pair<integral_c<int, 2>, integral_c<char, 'b'> >,
    pair<integral_c<int, 3>, integral_c<char, 'c'> >
> compile_time_map;

With pre-C++0x, the closest thing you can get is by not using containers designed for runtime usage (and limiting yourself to fundamental types and aggregates):

struct pair { int first; char second; };
pair arr[] = {{1,'a'}, {2,'b'}}; // assuming arr has static storage

This could then be accessed using some kind of map view, or you could implement a wrapper that allows for aggregate initialization similar to what Boost.Array does.

Of course the question is wether the benefits justify the time put into implementing this.

If my reading is correct here, C++0x initializer-lists may give you static initialization of non-aggregates like std::map and std::pair, but only if that doesn't change semantics compared to dynamic initialization.
Thus, it seems to me you can only get what you asked for if your implementation can verify via static analysis that the behaviour doesn't change if the map is statically initialized, but no guarantees that it happens.

There is a trick you can use, but only if this data will not be used in any other static constructor. First define a simple class like this:

typedef void (*VoidFunc)();
class Initializer
{
  public:
    Initializer(const VoidFunc& pF)
    {
      pF();
    }
};


Then, use it like this:

std::map<std::string, int> numbers;
void __initNumsFunc()
{
  numbers["one"] = 1;
  numbers["two"] = 2;
  numbers["three"] = 3;
}
Initializer __initNums(&__initNumsFunc);


Of course this is a bit overkill so I would recommend using it only if you really have to.

There is no standard way to initialize std::map at compile time. As others have mentioned, C++0x will allow the compiler to optimize the initialization to be static if possible, but that will never be guaranteed.

Remember, though, that the STL is just an interface spec. You can create your own compliant containers and give them static initialization capability.

Depending on whether you plan on upgrading your compiler and STL implementation (particularly on an embedded platform), you might even just dig into the implementation you're using, add derived classes, and use those!

template <const int N> struct Map  { enum { value = N}; };
template <> struct Map <1> { enum { value = (int)'a'}; };
template <> struct Map <2> { enum { value = (int)'b'}; };
template <> struct Map <3> { enum { value = (int)'c'}; };

std::cout  << Map<1>::value ;
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!