My use-case: I want to build a constexpr map with constant content and size declared at compile time. I'm using C++ 14. This is my current basic approach on that container:
template <typename KeyType, typename ValueType, size_t size>
struct ConstantMap
{
using PairType = std::pair<KeyType, ValueType>;
using StorageType = std::array<PairType, size>;
const StorageType storage;
constexpr ValueType at (const KeyType& key) const
{
const auto it = std::find_if (storage.begin(), storage.end(), [&key] (const auto& v) { return v.first == key; });
if (it != storage.end())
return it.second;
throw std::range_error ("ConstantMap: Key not found");
}
constexpr ValueType operator[] (const KeyType& key) const { return at (key); }
};
What works is initializing it like this:
constexpr std::array<std::pair<int, int>, 2> values =
{{
{ 1, 2 },
{ 3, 4 }
}};
constexpr ConstantMap<int, int, 2> myMap {{ values }};
I'd like to simplify this by a makeConstantMap
function that takes a parameter pack of pairs and returns a map with the correct size and key/value types, like this:
constexpr auto myMap = makeConstantMap<int, int> ({ 1, 2 }, { 3, 4 });
My approach on that is
template <typename KeyType, typename ValueType, typename... Values>
constexpr ConstantMap<KeyType, ValueType, sizeof...(Values)> makeConstantMap (std::pair<KeyType, Values>&&... pairs)
{
return {{ std::forward<std::pair<KeyType, Values>> (pairs)... }};
}
Which fails with candidate template ignored: substitution failure [with KeyType = int, ValueType = int]: deduced incomplete pack <(no value), (no value)> for template parameter 'Values'
. Live example here.
It seems that my assumption that a parameter pack as template argument in std::pair
should create a parameter pack typed to std::pair
is wrong. How do I get this working or is it even possible to make it work the way I'd like it to?
Maybe passing through an old C-style array?
template <typename KT, typename VT, std::size_t Dim, std::size_t ... Is>
constexpr auto mcp_helper (std::pair<KT, VT> const (& arr)[Dim],
std::index_sequence<Is...>)
{ return ConstantMap<KT, VT, Dim>{{ arr[Is]... }}; }
template <typename KT, typename VT, std::size_t Dim>
constexpr auto makeConstantMap (std::pair<KT, VT> const (& arr)[Dim])
{ return mcp_helper<KT, VT>(arr, std::make_index_sequence<Dim>{}); }
// ...
auto x = makeConstantMap<int, int>({{1, 2}, {3, 4}});