Не удалось вывести аргумент шаблона с частично специализированным шаблоном с использованием 'auto' - программирование

Не удалось вывести аргумент шаблона с частично специализированным шаблоном с использованием 'auto'

Для забавы я создал очень простой класс карты типа-значения времени компиляции, как показано ниже:

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 следует рассматривать как разные типы, а не конфликтующие) или это неправильное понимание моего сторона того, что возможно с выводом аргументов шаблона (я не адвокат по языку!), или какая-то другая ошибка в моей реализации?

4b9b3361

Ответ 1

Проблема в том, что Values::A можно преобразовать в 0 с помощью "преобразованного константного выражения".

Из стандарта С++ 14 (n4140 раздел 5.19, пункт 3, стр. 133):

Преобразованное константное выражение типа T является выражением, неявно преобразуемым в значение типа T, где преобразованное выражение является основным константным выражением, а последовательность неявного преобразования содержит только определенные пользователем преобразования, преобразования lvalue-в-значение (4.1), интегральные продвижения (4.5) и интегральные преобразования (4.7), кроме сужающих преобразований (8.5.4).

[Примечание: такие выражения могут использоваться в новых выражениях (5.3.4), в качестве выражений падежа (6.4.2), в качестве инициализаторов перечислителя, если базовый тип является фиксированным (7.2), в качестве границ массива (8.3.4) и как целочисленные или перечислительные нетиповые аргументы шаблона (14.3). —Конец примечания]

(мой акцент)

В результате между 0 и Values::A возникает неоднозначность перегрузки.