出于一点乐趣,我创建了一个非常基本的编译时类型值映射类,如下所示:
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
应该被视为不同的类型且不应冲突)是否导致无法在第一个实现中推断出模板参数?我对模板参数推导(我不是语言律师!)或实现过程中的其他错误有什么误解?
答案 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)。 —尾注]
(我的重点)
结果是0
和Values::A
之间存在过载歧义。