While (re)implementing a simple constexpr map, I wrote this (godbolt):
template <class key_type, class value_type, int N>
class flat_map
{
private:
struct pair
{
key_type key;
value_type value;
};
const pair elements[N];
public:
consteval flat_map(const pair (&arr)[N]) noexcept
: elements(arr) // works on gcc?!
{}
[[nodiscard]] consteval value_type operator[](const key_type key) const
{
for (const pair &elem : elements)
if (elem.key == key)
return elem.value;
throw "Key not found";
}
};
constexpr flat_map<int, char, 3> m = {{
{ 4, 'a' }, { -1, 'b' }, { 42, 'c' }
}};
static_assert(m[4] == 'a');
static_assert(m[-1] == 'b');
static_assert(m[42] == 'c');
int main()
{
return m[4]; // 97=='a'
}
I naively thought to set the private array elements as const and initialize it in the constructor; I was using gcc trunk as compiler, and all was seemingly working well.
When I decided to try it with msvc and clang, I had compilation errors: both were complaining about the array initialization requiring a brace-enclosed initializer list.
In hindsight the other compilers aren't particularly wrong, are they? Am I inadvertently using some gcc non standard extensions here?
Ehm, by the way, what would you do to avoid copying the array elements by hand?
[class.base.init]/7:
[dcl.init.general]/16.5:
Point 16.5 takes precedence over all points that follow, in particular those that cover copy-initialization from a value of the same type (16.6 and 16.9). As such, an array data member can only be initialized by initializing each of the array’s elements individually. GCC’s behaviour is therefore non-conforming.