For a bit of fun, I created a very basic compile-time type-value map class, as follows:
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));
};
Run Code Online (Sandbox Code Playgroud)
to be used in a way such as this:
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, ""); //***
Run Code Online (Sandbox Code Playgroud)
Unfortunately, the two lines marked //*** fail with the error failed template argument deduction or similar on clang and g++ (the two compilers I have to hand). I can understand why this might be because Values::A has the value 0 so the two potentially collide. However, I would argue that they are in fact different types – one is plain integer, the other an enum class with underlying type integer – and so shouldn't in fact collide.
If I implement my map class differently, like so:
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));
};
Run Code Online (Sandbox Code Playgroud)
then there are no template argument deduction errors.
Therefore the question is, is the failure to deduce the template argument in the first implementation due to a bug in the compilers (given my assertion that integer and enum class should be treated as different types and not collide) or is it a misunderstanding on my side of what is possible with template argument deduction (I am not a language lawyer!), or some other bug in my implementation?
问题是可以通过“转换后的常量表达式”Values::A进行转换。0
来自 C++14 标准(n4140第 5.19 节,第 3 段,第 133 页):
\n\n\nT 类型的已转换常量表达式是隐式转换为 T 类型纯右值的表达式,其中已转换表达式是核心常量表达式,隐式转换序列仅包含用户定义的转换、左值到右值的转换 (4.1) 、积分促销 (4.5) 以及除缩小转换 (8.5.4) 之外的积分转换 (4.7)。
\n[ 注意:此类表达式可以在 new 表达式 (5.3.4)、case 表达式 (6.4.2)、枚举器初始值设定项(如果基础类型固定)中使用 (7.2)、数组边界 (8.3.4) 和整型或枚举非类型模板参数(14.3)。\xe2\x80\x94结束注]
\n
(我的重点)
\n结果是0和之间存在过载歧义Values::A。