簡體   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