[英]In C++, how can one map between enum values and data types, so the types can be used in templates?
如何做到这一点,这显然是不可能的 C++,在真实的 C++ 中?:
Type decodeUiEnum(UiEnum myEnum) { // impossible: cannot return a data type
// one switch statement to rule them all
switch(myEnum) {
case USER_SELECTED_GREYSCALE: return GreyscalePixel;
case USER_SELECTED_RGB: return RgbPixel;
...
}
}
void doSomeGraphicsMagic1(UiEnum myEnum) {
...
Foo<decodeUiEnum(myEnum)> a(...); // impossible: type not available at compile
// time
...
}
void doSomeGraphicsMagic2(UiEnum myEnum, int blah) {
...
Bar<int, decodeUiEnum(myEnum)> b(...); // impossible
...
}
等等,所以您可以只向顶部的 switch 语句添加新类型,而不必修改它下面的其他代码,当然只要该代码是适当的通用代码就可以了? 否则,您需要在每个 function 中使用一个 switch 语句来将必要的类型映射到模板中,这不是那么多可维护的代码,而且有很多重复。 所以更一般地说 - 如果这是以错误的方式接近它,我们如何实现代码的预期属性?
也就是说,我想要做的是,在一个以枚举作为参数的 function 中,实例化一个模板类型,其中模板参数取决于枚举,而不是在每个 function 中都有一个 switch-on-enum。
是的,这实际上是可能的。
技巧基于部分模板规范, std::get使用的这种方法
例如:
#include <iostream>
// specify an enumeration we will use as type index and related data types
enum class UiEnum {
GRAY_SCALE,
RGB_PIXEL
};
struct GreyscalePixel;
struct RgbPixel;
// make base template class
template<UiEnum _EV>
struct ui_enum_type {
};
// do partial type specification trick
// insert typedefs with data type we need for each enumeration value
template<>
struct ui_enum_type<UiEnum::GRAY_SCALE> {
typedef GreyscalePixel pixel_type;
};
template<>
struct ui_enum_type<UiEnum::RGB_PIXEL> {
typedef RgbPixel pixel_type;
};
// demo classes to demonstrate how trick is working at runtime
template<typename T>
struct demo_class {
};
template <>
struct demo_class<GreyscalePixel> {
demo_class()
{
std::cout << "GreyscalePixel" << std::endl;
}
};
template <>
struct demo_class<RgbPixel> {
demo_class()
{
std::cout << "RgbPixel" << std::endl;
}
};
// use swithc trick
static void swich_trick(std::size_t runtimeValue)
{
switch( static_cast<UiEnum>(runtimeValue) ) {
case UiEnum::GRAY_SCALE: {
demo_class< ui_enum_type<UiEnum::GRAY_SCALE>::pixel_type > demo1;
}
break;
case UiEnum::RGB_PIXEL: {
demo_class< ui_enum_type<UiEnum::RGB_PIXEL>::pixel_type > demo2;
}
break;
}
}
int main(int argc, const char** argv)
{
// Do runtime based on the trick, use enum instead of data type
for(std::size_t i=0; i < 2; i++) {
swich_trick(i);
}
return 0;
}
无论如何,我的建议是使用经典的多态性而不是复杂化的模板元编程。 大多数现代编译器在优化期间进行去虚拟化。 例如:
#include <iostream>
#include <memory>
#include <unordered_map>
enum class UiEnum {
GRAY_SCALE,
RGB_PIXEL
};
class GraphicsMagic {
GraphicsMagic(const GraphicsMagic&) = delete;
GraphicsMagic& operator=(const GraphicsMagic&) = delete;
protected:
GraphicsMagic() = default;
public:
virtual ~GraphicsMagic( ) = default;
virtual void doSome() = 0;
};
class GreyscaleGraphicsMagic final: public GraphicsMagic {
public:
GreyscaleGraphicsMagic():
GraphicsMagic()
{
}
virtual void doSome() override
{
std::cout << "GreyscalePixel" << std::endl;
}
};
class RgbGraphicsMagic final: public GraphicsMagic {
public:
RgbGraphicsMagic():
GraphicsMagic()
{
}
virtual void doSome() override
{
std::cout << "RgbPixel" << std::endl;
}
};
int main(int argc, const char** argv)
{
std::unordered_map< UiEnum, std::shared_ptr< GraphicsMagic > > handlers;
handlers.emplace(UiEnum::GRAY_SCALE, new GreyscaleGraphicsMagic() ) ;
handlers.emplace(UiEnum::RGB_PIXEL, new RgbGraphicsMagic() );
for(std::size_t i=0; i < 2; i++) {
handlers.at( static_cast<UiEnum>(i) )->doSome();
}
return 0;
}
您可以使用std::variant
,然后使用代码std::visit
该变体。
首先,我们想要一个“将类型作为参数传递”的模板
template <typename T>
struct tag {
using type = T;
};
然后我们为它定义我们的变体和工厂。
using PixelType = std::variant<tag<GreyscalePixel>, tag<RgbPixel>>;
PixelType decodeUiEnum(UiEnum myEnum) {
switch(myEnum) {
case USER_SELECTED_GREYSCALE: return tag<GreyscalePixel>{};
case USER_SELECTED_RGB: return tag<RgbPixel>{};
...
}
}
现在我们的方法可以写成PixelType
的访问者
void doSomeGraphicsMagic1(UiEnum myEnum) {
std::visit([](auto t){
using Pixel = decltype(t)::type;
Foo<Pixel> a(...);
}, decodeUiEnum(myEnum));
}
int doSomeGraphicsMagic2(UiEnum myEnum, int blah) {
return std::visit([blah](auto t){
using Pixel = decltype(t)::type;
Bar<int, Pixel> a(...);
return a.frob();
}, decodeUiEnum(myEnum));
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.