Для забавы я создал очень простой класс карты типа-значения времени компиляции, как показано ниже:
template <typename T, auto V>
struct TypeValuePair { };
template <typename... TypeValuePairs>
struct TypeValueMap
{
struct MapItems : TypeValuePairs... { };
template <typename T, auto V>
static constexpr auto Lookup(TypeValuePair<T, V>*)
{ return V; }
template <auto V, typename T>
static T Lookup(TypeValuePair<T, V>*);
template <typename T>
static constexpr auto ValueFor = Lookup<T>((MapItems*)nullptr);
template <auto V>
using TypeFor = decltype(Lookup<V>((MapItems*)nullptr));
};
для использования таким образом, как это:
struct A; struct B; struct C;
enum class Values { A, B, C };
using Map = TypeValueMap<
TypeValuePair<A, Values::A>,
TypeValuePair<B, Values::B>,
TypeValuePair<C, Values::C>,
TypeValuePair<struct Other, 0>
>;
static_assert(Map::ValueFor<A> == Values::A, "");
static_assert(Map::ValueFor<B> == Values::B, "");
static_assert(Map::ValueFor<C> == Values::C, "");
static_assert(Map::ValueFor<struct Other> == 0, "");
static_assert(std::is_same<Map::TypeFor<Values::A>, A>::value, ""); //***
static_assert(std::is_same<Map::TypeFor<Values::B>, B>::value, "");
static_assert(std::is_same<Map::TypeFor<Values::C>, C>::value, "");
static_assert(std::is_same<Map::TypeFor<0>, struct Other>::value, ""); //***
К сожалению, две строки, помеченные //***
терпят неудачу из-за ошибки: не удалось вывести аргумент шаблона или что-то подобное для clang и g++ (два компилятора, которые я должен передать) Я могу понять, почему это может быть связано с тем, что Values::A
имеет значение 0
поэтому эти два потенциально сталкиваются. Тем не менее, я бы сказал, что это на самом деле разные типы - один является простым целым числом, а другой - enum class
с базовым целым типом - и поэтому фактически не должен сталкиваться.
Если я реализую свой класс карты по-другому, вот так:
template <typename T, auto V>
struct TypeValuePair
{
protected:
static constexpr auto Lookup(T*)
{ return V; }
template <template <auto> class Wrapper>
static T Lookup(Wrapper<V>*);
};
template <typename... TypeValuePairs>
struct TypeValueMap
{
struct MapItems : TypeValuePairs...
{ using TypeValuePairs::Lookup...; };
template <auto> struct LookupByValue;
template <typename T>
static constexpr auto ValueFor = MapItems::Lookup((T*)nullptr);
template <auto V>
using TypeFor = decltype(MapItems::Lookup((LookupByValue<V>*)nullptr));
};
тогда нет ошибок вывода аргументов шаблона.
Поэтому вопрос заключается в том, является ли невозможность вывести аргумент шаблона в первой реализации из-за ошибки в компиляторах (учитывая мое утверждение, что целочисленный и enum class
следует рассматривать как разные типы, а не конфликтующие) или это неправильное понимание моего сторона того, что возможно с выводом аргументов шаблона (я не адвокат по языку!), или какая-то другая ошибка в моей реализации?