繁体   English   中英

在C++中,枚举值和数据类型之间的map如何才能在模板中使用这些类型?

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM