使用'auto'的部分专用模板无法进行模板参数推导失败

时间:2019-06-14 06:44:11

标签: c++

出于一点乐趣,我创建了一个非常基本的编译时类型值映射类,如下所示:

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应该被视为不同的类型且不应冲突)是否导致无法在第一个实现中推断出模板参数?我对模板参数推导(我不是语言律师!)或实现过程中的其他错误有什么误解?

1 个答案:

答案 0 :(得分:0)

问题在于Values::A可以通过“转换后的常量表达式”转换为0

根据C ++ 14标准(n4140第5.19节,第3页,第133页):

  

类型T的已转换常量表达式是一个隐式转换为类型T的prvalue的表达式,其中已转换的表达式是核心常量表达式,而隐式转换序列仅包含用户定义的转换,即左值到右值转换(4.1),积分促销(4.5)和积分转化(4.7),而不是缩小转化(8.5.4)。

     

[注意:此类表达式可在新表达式(5.3.4)中用作case表达式(6.4.2),在基础类型为固定的情况下用作枚举器初始化器(7.2),作为数组边界(8.3.4) ,并作为整数或枚举非类型模板参数(14.3)。 —尾注]

(我的重点)

结果是0Values::A之间存在过载歧义。