定义基于模板的映射指针,以便在验证范围时将 int 解析为枚举
Define template based map pointer in order to parse int to enum while validating range
我想提供一个简单的机制,将 int 转换为枚举类型,并在此过程中提供一些错误检查。这将确保 int 不会超出其声明的枚举范围。我想出了以下函数,它利用了 std::map:
#include <map>
#include <type_traits>
enum A
{
a = 0,
b,
c
};
enum B
{
d = 10,
e,
f
};
std::map<int, A> mA;
std::map<int, B> mB;
template<typename T> T ParseEnum(int nVal)
{
std::map<int, T>* p;
if (std::is_same<T, A>::value)
p = &mA; //compiler error
else if (std::is_same<T, B>::value)
p = &mB; //compiler error
return static_cast<T>(p->at(nVal));
}
int _tmain(int argc, _TCHAR* argv[])
{
mA.insert(std::pair<int, A>(0, A::a));
mA.insert(std::pair<int, A>(1, A::b));
mA.insert(std::pair<int, A>(2, A::c));
mB.insert(std::pair<int, B>(10, B::d));
mB.insert(std::pair<int, B>(11, B::e));
mB.insert(std::pair<int, B>(12, B::f));
try
{
A eA = ParseEnum<A>(1); //ok, should resolve to A::b;
B eB = ParseEnum<B>(16); //should throw an exception;
}
catch (std::out_of_range&)
{
}
return 0;
}
不幸的是,我在将地图引用分配给基于模板的地图指针时遇到问题,如以下编译器错误所示:
error C2440: '=' : cannot convert from 'std::map<int,A,std::less<_Kty>,std::allocator<std::pair<const _Kty,_Ty>>> *' to 'std::map<int,B,std::less<_Kty>,std::allocator<std::pair<const _Kty,_Ty>>> *'
error C2440: '=' : cannot convert from 'std::map<int,B,std::less<_Kty>,std::allocator<std::pair<const _Kty,_Ty>>> *' to 'std::map<int,A,std::less<_Kty>,std::allocator<std::pair<const _Kty,_Ty>>> *'
有没有办法定义这样一个基于模板的指针,还是我运气不好?
模板专业化更适合:
//declare the ParseEnum function template
template<typename T>
T ParseEnum(int nVal);
//specialization for when ParseEnum is instantiated with A
template<>
A ParseEnum<A> (int nVal)
{
return mA.at(nVal);
}
//specialization for B
template<>
B ParseEnum<B> (int nVal)
{
return mB.at(nVal);
}
或者这可能更灵活。它创建一个 struct
,它将保存对 map
的引用,该 map
对应于其模板参数:
//declare a struct which will hold a reference to the map we want
template <typename T>
struct GetMap
{
static std::map<int,T> ↦
};
//when instantiated with A, the map member is a reference to mA
template<>
std::map<int,A> &GetMap<A>::map = mA;
//similarly for B and mB
template<>
std::map<int,B> &GetMap<B>::map = mB;
template<typename T> T ParseEnum(int nVal)
{
//GetMap<T>::map will be a reference to the correct map
return GetMap<T>::map.at(nVal);
}
解决问题的一种方法:
template <typename T> struct MapPtr;
template <> struct MapPtr<A>
{
static constexpr std::map<int, A>* const value = &mA;
};
template <> struct MapPtr<B>
{
static constexpr std::map<int, B>* const value = &mB;
};
template<typename T> T ParseEnum(int nVal)
{
std::map<int, T>* ptr = MapPtr<T>::value;
return static_cast<T>(ptr->at(nVal));
}
I would like provide a simple mechanism which casts an int to an enum
type and also provides some error checking along the way. This would
make sure, that the int doesn't fall out its declared enum range.
这听起来像是对枚举的错误使用。理想情况下,从语义的角度来看,枚举应该只是具有一组固定值的独特类型,并且它作为 int
实现的事实应该是 实现细节 (很像私有成员变量)。
您可能想查看 C++11 enum classes。
return static_cast<T>(p->at(nVal));
at
从何而来?您需要这样的东西才能抛出 std::out_of_range
:
std::map<int, T>::iterator find_iter = p->find(nVal);
if (find_iter == p->end())
{
throw std::out_of_range("illegal value");
}
return static_cast<T>(find_iter->second);
(C++11 为 std::map
提供了 at
)
int _tmain(int argc, _TCHAR* argv[])
在标准 C++ 中应该是 int main(int argc, char* argv[])
。或者只是 int main()
,因为您无论如何都不使用参数。
mA.insert(std::pair<int, A>(0, A::a));
您应该使用 std::make_pair
让模板类型推导自动计算出参数类型,即:
mA.insert(std::make_pair(0, A::a));
现在让我们看看导致编译器错误的原因:
template<typename T> T ParseEnum(int nVal)
{
std::map<int, T> * p;
if (std::is_same<T, A>::value)
p = &mA; //compiler error
else if (std::is_same<T, B>::value)
p = &mB; //compiler error
return static_cast<T>(p->at(nVal));
}
在此函数中,T
是 A
或 B
。在第一种情况下,它变成:
// pseudo code:
A ParseEnumA(int nVal)
{
std::map<int, A> * p;
if (std::is_same<A, A>::value)
p = &mA; // NOT a compiler error
else if (std::is_same<A, B>::value)
p = &mB; //compiler error
return static_cast<A>(p->at(nVal));
}
第二种情况:
// pseudo code:
B ParseEnumB(int nVal)
{
std::map<int, B> * p;
if (std::is_same<B, A>::value)
p = &mA; // compiler error
else if (std::is_same<B, B>::value)
p = &mB; // NOT a compiler error
return static_cast<B>(p->at(nVal));
}
看看会发生什么?在任何一种情况下,一个 的分配工作,另一个失败,因为 std::map<int, A>
和 std::map<int, B>
是不相关的类型。
Is there a way to define such a template based pointer or am I out of
luck here?
对于非常 快速而肮脏的 修复,您可以使用 reinterpret_cast
。它不会在 运行 时执行,但会消除编译时错误。
if (std::is_same<T, A>::value)
p = reinterpret_cast<std::map<int, T>*>(&mA);
else if (std::is_same<T, B>::value)
p = reinterpret_cast<std::map<int, T>*>(&mB);
完整程序如下:
#include <map>
#include <stdexcept>
#include <iostream>
enum A
{
a = 0,
b,
c
};
enum B
{
d = 10,
e,
f
};
std::map<int, A> mA;
std::map<int, B> mB;
template<typename T> T ParseEnum(int nVal)
{
std::map<int, T>* p;
if (std::is_same<T, A>::value)
p = reinterpret_cast<std::map<int, T>*>(&mA);
else if (std::is_same<T, B>::value)
p = reinterpret_cast<std::map<int, T>*>(&mB);
std::map<int, T>::iterator find_iter = p->find(nVal);
if (find_iter == p->end())
{
throw std::out_of_range("illegal value");
}
return static_cast<T>(find_iter->second);
}
int main()
{
mA.insert(std::make_pair(0, A::a));
mA.insert(std::make_pair(1, A::b));
mA.insert(std::make_pair(2, A::c));
mB.insert(std::make_pair(10, B::d));
mB.insert(std::make_pair(11, B::e));
mB.insert(std::make_pair(12, B::f));
try
{
A eA = ParseEnum<A>(1); //ok, should resolve to A::b;
std::cout << "OK\n";
B eB = ParseEnum<B>(16); //should throw an exception;
}
catch (std::out_of_range const& exc)
{
std::cerr << exc.what() << "\n";
}
}
但问题是:这是一个好的解决方案吗?
我不这么认为。您应该使用 C++11 枚举 类 和 消除将 int
转换为 enum
的必要性!考虑 int
一个实现细节,比如私有成员变量。
这里有一个不同的策略来处理对应于A
和B
的map
。使用 class 模板提供对 map
的访问,而不是将它们作为全局数据。
#include <iostream>
#include <type_traits>
#include <map>
#include <stdexcept>
enum A
{
a = 0,
b,
c
};
enum B
{
d = 10,
e,
f
};
template <typename T> struct MapContainer
{
static std::map<int, T>& getMap()
{
static std::map<int, T> theMap;
return theMap;
}
};
template<typename T> T ParseEnum(int nVal)
{
std::map<int, T>& theMap = MapContainer<T>::getMap();
return static_cast<T>(theMap.at(nVal));
}
int main(int argc, char* argv[])
{
std::map<int, A>& mA = MapContainer<A>::getMap();
mA.insert(std::pair<int, A>(0, A::a));
mA.insert(std::pair<int, A>(1, A::b));
mA.insert(std::pair<int, A>(2, A::c));
std::map<int, B>& mB = MapContainer<B>::getMap();
mB.insert(std::pair<int, B>(10, B::d));
mB.insert(std::pair<int, B>(11, B::e));
mB.insert(std::pair<int, B>(12, B::f));
try
{
A eA = ParseEnum<A>(1); //ok, should resolve to A::b;
std::cout << "ParseEnum<A>(1): " << eA << std::endl;
B eB = ParseEnum<B>(16); //should throw an exception;
std::cout << "ParseEnum<B>(16): " << eB << std::endl;
}
catch (std::out_of_range&)
{
}
return 0;
}
我想提供一个简单的机制,将 int 转换为枚举类型,并在此过程中提供一些错误检查。这将确保 int 不会超出其声明的枚举范围。我想出了以下函数,它利用了 std::map:
#include <map>
#include <type_traits>
enum A
{
a = 0,
b,
c
};
enum B
{
d = 10,
e,
f
};
std::map<int, A> mA;
std::map<int, B> mB;
template<typename T> T ParseEnum(int nVal)
{
std::map<int, T>* p;
if (std::is_same<T, A>::value)
p = &mA; //compiler error
else if (std::is_same<T, B>::value)
p = &mB; //compiler error
return static_cast<T>(p->at(nVal));
}
int _tmain(int argc, _TCHAR* argv[])
{
mA.insert(std::pair<int, A>(0, A::a));
mA.insert(std::pair<int, A>(1, A::b));
mA.insert(std::pair<int, A>(2, A::c));
mB.insert(std::pair<int, B>(10, B::d));
mB.insert(std::pair<int, B>(11, B::e));
mB.insert(std::pair<int, B>(12, B::f));
try
{
A eA = ParseEnum<A>(1); //ok, should resolve to A::b;
B eB = ParseEnum<B>(16); //should throw an exception;
}
catch (std::out_of_range&)
{
}
return 0;
}
不幸的是,我在将地图引用分配给基于模板的地图指针时遇到问题,如以下编译器错误所示:
error C2440: '=' : cannot convert from 'std::map<int,A,std::less<_Kty>,std::allocator<std::pair<const _Kty,_Ty>>> *' to 'std::map<int,B,std::less<_Kty>,std::allocator<std::pair<const _Kty,_Ty>>> *'
error C2440: '=' : cannot convert from 'std::map<int,B,std::less<_Kty>,std::allocator<std::pair<const _Kty,_Ty>>> *' to 'std::map<int,A,std::less<_Kty>,std::allocator<std::pair<const _Kty,_Ty>>> *'
有没有办法定义这样一个基于模板的指针,还是我运气不好?
模板专业化更适合:
//declare the ParseEnum function template
template<typename T>
T ParseEnum(int nVal);
//specialization for when ParseEnum is instantiated with A
template<>
A ParseEnum<A> (int nVal)
{
return mA.at(nVal);
}
//specialization for B
template<>
B ParseEnum<B> (int nVal)
{
return mB.at(nVal);
}
或者这可能更灵活。它创建一个 struct
,它将保存对 map
的引用,该 map
对应于其模板参数:
//declare a struct which will hold a reference to the map we want
template <typename T>
struct GetMap
{
static std::map<int,T> ↦
};
//when instantiated with A, the map member is a reference to mA
template<>
std::map<int,A> &GetMap<A>::map = mA;
//similarly for B and mB
template<>
std::map<int,B> &GetMap<B>::map = mB;
template<typename T> T ParseEnum(int nVal)
{
//GetMap<T>::map will be a reference to the correct map
return GetMap<T>::map.at(nVal);
}
解决问题的一种方法:
template <typename T> struct MapPtr;
template <> struct MapPtr<A>
{
static constexpr std::map<int, A>* const value = &mA;
};
template <> struct MapPtr<B>
{
static constexpr std::map<int, B>* const value = &mB;
};
template<typename T> T ParseEnum(int nVal)
{
std::map<int, T>* ptr = MapPtr<T>::value;
return static_cast<T>(ptr->at(nVal));
}
I would like provide a simple mechanism which casts an int to an enum type and also provides some error checking along the way. This would make sure, that the int doesn't fall out its declared enum range.
这听起来像是对枚举的错误使用。理想情况下,从语义的角度来看,枚举应该只是具有一组固定值的独特类型,并且它作为 int
实现的事实应该是 实现细节 (很像私有成员变量)。
您可能想查看 C++11 enum classes。
return static_cast<T>(p->at(nVal));
at
从何而来?您需要这样的东西才能抛出 std::out_of_range
:
std::map<int, T>::iterator find_iter = p->find(nVal);
if (find_iter == p->end())
{
throw std::out_of_range("illegal value");
}
return static_cast<T>(find_iter->second);
(C++11 为 std::map
提供了 at
)
int _tmain(int argc, _TCHAR* argv[])
在标准 C++ 中应该是 int main(int argc, char* argv[])
。或者只是 int main()
,因为您无论如何都不使用参数。
mA.insert(std::pair<int, A>(0, A::a));
您应该使用 std::make_pair
让模板类型推导自动计算出参数类型,即:
mA.insert(std::make_pair(0, A::a));
现在让我们看看导致编译器错误的原因:
template<typename T> T ParseEnum(int nVal) { std::map<int, T> * p; if (std::is_same<T, A>::value) p = &mA; //compiler error else if (std::is_same<T, B>::value) p = &mB; //compiler error return static_cast<T>(p->at(nVal)); }
在此函数中,T
是 A
或 B
。在第一种情况下,它变成:
// pseudo code:
A ParseEnumA(int nVal)
{
std::map<int, A> * p;
if (std::is_same<A, A>::value)
p = &mA; // NOT a compiler error
else if (std::is_same<A, B>::value)
p = &mB; //compiler error
return static_cast<A>(p->at(nVal));
}
第二种情况:
// pseudo code:
B ParseEnumB(int nVal)
{
std::map<int, B> * p;
if (std::is_same<B, A>::value)
p = &mA; // compiler error
else if (std::is_same<B, B>::value)
p = &mB; // NOT a compiler error
return static_cast<B>(p->at(nVal));
}
看看会发生什么?在任何一种情况下,一个 的分配工作,另一个失败,因为 std::map<int, A>
和 std::map<int, B>
是不相关的类型。
Is there a way to define such a template based pointer or am I out of luck here?
对于非常 快速而肮脏的 修复,您可以使用 reinterpret_cast
。它不会在 运行 时执行,但会消除编译时错误。
if (std::is_same<T, A>::value)
p = reinterpret_cast<std::map<int, T>*>(&mA);
else if (std::is_same<T, B>::value)
p = reinterpret_cast<std::map<int, T>*>(&mB);
完整程序如下:
#include <map>
#include <stdexcept>
#include <iostream>
enum A
{
a = 0,
b,
c
};
enum B
{
d = 10,
e,
f
};
std::map<int, A> mA;
std::map<int, B> mB;
template<typename T> T ParseEnum(int nVal)
{
std::map<int, T>* p;
if (std::is_same<T, A>::value)
p = reinterpret_cast<std::map<int, T>*>(&mA);
else if (std::is_same<T, B>::value)
p = reinterpret_cast<std::map<int, T>*>(&mB);
std::map<int, T>::iterator find_iter = p->find(nVal);
if (find_iter == p->end())
{
throw std::out_of_range("illegal value");
}
return static_cast<T>(find_iter->second);
}
int main()
{
mA.insert(std::make_pair(0, A::a));
mA.insert(std::make_pair(1, A::b));
mA.insert(std::make_pair(2, A::c));
mB.insert(std::make_pair(10, B::d));
mB.insert(std::make_pair(11, B::e));
mB.insert(std::make_pair(12, B::f));
try
{
A eA = ParseEnum<A>(1); //ok, should resolve to A::b;
std::cout << "OK\n";
B eB = ParseEnum<B>(16); //should throw an exception;
}
catch (std::out_of_range const& exc)
{
std::cerr << exc.what() << "\n";
}
}
但问题是:这是一个好的解决方案吗?
我不这么认为。您应该使用 C++11 枚举 类 和 消除将 int
转换为 enum
的必要性!考虑 int
一个实现细节,比如私有成员变量。
这里有一个不同的策略来处理对应于A
和B
的map
。使用 class 模板提供对 map
的访问,而不是将它们作为全局数据。
#include <iostream>
#include <type_traits>
#include <map>
#include <stdexcept>
enum A
{
a = 0,
b,
c
};
enum B
{
d = 10,
e,
f
};
template <typename T> struct MapContainer
{
static std::map<int, T>& getMap()
{
static std::map<int, T> theMap;
return theMap;
}
};
template<typename T> T ParseEnum(int nVal)
{
std::map<int, T>& theMap = MapContainer<T>::getMap();
return static_cast<T>(theMap.at(nVal));
}
int main(int argc, char* argv[])
{
std::map<int, A>& mA = MapContainer<A>::getMap();
mA.insert(std::pair<int, A>(0, A::a));
mA.insert(std::pair<int, A>(1, A::b));
mA.insert(std::pair<int, A>(2, A::c));
std::map<int, B>& mB = MapContainer<B>::getMap();
mB.insert(std::pair<int, B>(10, B::d));
mB.insert(std::pair<int, B>(11, B::e));
mB.insert(std::pair<int, B>(12, B::f));
try
{
A eA = ParseEnum<A>(1); //ok, should resolve to A::b;
std::cout << "ParseEnum<A>(1): " << eA << std::endl;
B eB = ParseEnum<B>(16); //should throw an exception;
std::cout << "ParseEnum<B>(16): " << eB << std::endl;
}
catch (std::out_of_range&)
{
}
return 0;
}