1 // { dg-do compile }
2 // { dg-options "-O3 -fdump-tree-optimized -std=c++14" }
3 
4 #include <array>
5 #include <utility>
6 
7 
8 template<class Key, class T, size_t N> struct static_map
9 {
10   using key_type = Key;
11   using mapped_type = T;
12   using value_type = std::pair<const key_type, mapped_type>;
13 private:
14   using _value_type = std::pair<size_t, value_type>;
15   _value_type _values[N];
_new_value_typestatic_map16   static constexpr _value_type _new_value_type(const std::pair<Key, T> &v)
17   {
18     return std::make_pair(0, std::make_pair(v.first, v.second));
19   }
20 public:
static_mapstatic_map21   template<class... U> constexpr static_map(U &&...il) : _values{ _new_value_type(il)... } { }
22   constexpr mapped_type &operator[](const key_type &k) { return at(k); }
23   constexpr const mapped_type &operator[](const key_type &k) const { return at(k); }
atstatic_map24   constexpr mapped_type &at(const key_type &k)
25   {
26     for (size_t n = 0; n < N; n++)
27       if (_values[n].second.first == k)
28         return _values[n].second.second;
29     throw std::out_of_range("Key not found");
30   }
atstatic_map31   constexpr const mapped_type &at(const key_type &k) const
32   {
33     for (size_t n = 0; n < N; n++)
34       if (_values[n].second.first == k)
35         return _values[n].second.second;
36     throw std::out_of_range("Key not found");
37   }
38 };
39 namespace detail
40 {
static_map_from_array(const std::pair<Key,T> (& il)[N],std::index_sequence<I...>)41   template<class Key, class T, size_t N, size_t... I> constexpr static_map<Key, T, N> static_map_from_array(const std::pair<Key, T>(&il)[N], std::index_sequence<I...>)
42   {
43     return static_map<Key, T, N>(il[I]...);
44   }
45 }
make_static_map(const std::pair<Key,T> (& il)[N])46 template<class Key, class T, size_t N> constexpr static_map<Key, T, N> make_static_map(const std::pair<Key, T> (&il)[N])
47 {
48   return detail::static_map_from_array<Key, T, N>(il, std::make_index_sequence<N>());
49 }
50 
51 /* Two phase construction, required because heterogeneous braced init
52 in C++ 14 has a big limitation: template<class... Args> auto make(Args &&...)
53 will accept make({ 5, "apple" }) as make(int, const char *) but
54 make({ 5, "apple" }, { 8, "pear" }) will fail to deduce Args as a
55 heterogeneous initializer_list is not permitted. This forces something
56 like make(make_pair{ 5, "apple" }, make_pair{ 8, "pear" }, ...) which
57 is less succinct than using a constexpr C array for the nested braced init.
58 */
59 constexpr std::pair<const int, const char *> map_data[] = {
60   { 5, "apple" },
61   { 8, "pear" },
62   { 0, "banana" }
63 };
64 
cstrcmp(const char * a,const char * b)65 template<size_t N> constexpr int cstrcmp(const char *a, const char *b)
66 {
67   for (size_t n = 0; n < N; n++)
68   {
69     if (a[n] < b[n]) return -1;
70     if (a[n] > b[n]) return 1;
71   }
72   return 0;
73 }
74 
main(void)75 int main(void)
76 {
77   constexpr auto cmap = make_static_map(map_data);
78   // No abort() appears in assembler, so this was executed constexpr
79   if(!cmap[8]) abort();
80   // This however does cause code implementing a lookup to be generated,
81   // so this was NOT executed constexpr
82   //const char *foo=cmap[5];
83   return 0;
84 }
85 
86 // { dg-final { scan-tree-dump-not "cmap" "optimized" { target x86_64-*-* i?86-*-* } } }
87