0

Simple question, how to initialize a map of arrays (or some other container type) of different sizes? For example:

enum class code {A,B,C};
enum class res {X1,X2,X3,X4,X5};

std::map<code, ?> name {
    {code:A, {res::X1,res::X2}},
    {code:B, {res::X2,res::X3, res::X4}},
    {code:C, {res::X5}}

};

I need to find at compile time if res::X2 is in map name at code::B

This expression should, I think, check for that using static_assert:

constexpr bool validate(code t, res p, int i = 0) {
    return (name[t][i] == p ? true : ((sizeof(name[t]) == (i+1)) ? false :validate(t, p, ++i)));
}

Because validate is a constexpr a basic array would work but how to define it in a map argument as an array of type res? And that each array could be different in size?

So, I've made a mistake. I was under the impression that map can be accessed in constexpr function. What container type can you suggest me that would allow me to achieve what I have written above?

CodeBreaker
  • 488
  • 9
  • 18

3 Answers3

2

If it is ok for the array to be const, this works:

std::map<code, std::initializer_list<res>> name {
    {code::A, {res::X1,res::X2}},
    {code::B, {res::X2,res::X3, res::X4}},
    {code::C, {res::X5}}
};

If you need to be able to write to the array, you'll need something like this:

std::map<code, std::vector<res>> name {
    {code::A, {res::X1,res::X2}},
    {code::B, {res::X2,res::X3, res::X4}},
    {code::C, {res::X5}}
};

This will come at the cost of additional memory allocations, since vector allocated on the heap.

If you want it to be writable and are ok with a fixed size, this also works and avoids the additional allocations:

std::map<code, std::array<res, 3>> name {
    {code::A, {res::X1,res::X2}},
    {code::B, {res::X2,res::X3, res::X4}},
    {code::C, {res::X5}}
};

As for the second part of your question, I'm not quite sure if any solution will work, given that you cannot access a map in a constexpr function.

H. Guijt
  • 3,325
  • 11
  • 16
  • Um, correct me if I am wrong but does the `std::initializer_list` in the map point to elements that no longer exist after the end of the expression? – NathanOliver Aug 01 '16 at 15:52
  • @NathanOliver That is a very good question. I was under the impression this is allowed, but if you are right I urgently need to rewrite some code... – H. Guijt Aug 01 '16 at 15:56
  • Didn't know about `std::initializer_list`. That helped. Thanks. – CodeBreaker Aug 03 '16 at 15:47
1

You can't use a std::map in a constexpr expression, not least because it has a non-trivial destructor (~map(); specified in the standard).

If you search the map section of the standard for constexpr, you won't find it (http://eel.is/c++draft/map), whereas you will for std::array (http://eel.is/c++draft/array.syn)

kfsone
  • 23,617
  • 2
  • 42
  • 74
0

Since std::map can't be used in constexpr expression I was forced to find another solution and thanks to @kfsone and @H. Guijt for pointing that out as well as mentioning std::initializer_list and std::array which lead me to this solution.

Since the data in the map in the question will be constant, i can do without an associative container. So I used a sorted array of std::initializer_lists together with a constexpr implementation of find function which works great for my needs.

template <class It, class T>
inline constexpr It sfind (It begin, It end, T const& value) noexcept
{
    return ! (begin != end && *begin != value)? begin : sfind (begin+1, end, value);
}

enum class code {A,B,C};
enum class res {X1,X2,X3,X4,X5};

constexpr std::array<std::initializer_list<res>, 3> name {{
    {res::X1,res::X2}, //code::A
    {res::X2,res::X3, res::X4}, //code::B
    {res::X5} // code::C

}};
const code c = code::A;
const res r = res::X3;
static_assert (name[static_cast<int>(c)].end () - sfind(name[static_cast<int>(c)].begin(), name[static_cast<int>(c)].end(), r) != 0,"Not found");
CodeBreaker
  • 488
  • 9
  • 18