【发布时间】:2019-10-28 18:36:09
【问题描述】:
为了好玩,我创建了一个非常基本的编译时类型-值映射类,如下:
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 应该被视为不同的类型而不是冲突)而未能在第一个实现中推断出模板参数,还是它我对模板参数推导可能发生的事情有误解(我不是语言律师!),还是我的实现中存在其他错误?
【问题讨论】:
-
与主要问题无关,但您可以简化:
struct MapItems : TypeValuePairs... { using TypeValuePairs::Lookup...; };. -
你确定clang和gcc会产生同样的错误吗?如果从映射中删除 A,两个编译器会发生什么情况?
-
感谢@Evg 的建议,这真是太好了!我已通过此更改更新了问题。
-
@n.m. - 是的,两个编译器都产生相同的错误;如果您从地图中删除 A,则“歧义”消失并且剩余地图解决
-
最小示例:godbolt.org/z/nIFKxY
标签: c++