【问题标题】:Can I Implement templated functions whose signatures are only different based on a type id?我可以实现签名仅基于类型 ID 不同的模板化函数吗?
【发布时间】:2012-12-28 13:54:09
【问题描述】:

这里有一些枚举类:

enum class Race : char {AINU, ELF, DWARF, MAN, EAGLE, HOBBIT, ENT, ORC, WIZARD};
enum class Color: char {RED, ORANGE, YELLOW, GREEN, BLUE, PURPLE};
enum class Direction: char{UP, DOWN, LEFT, RIGHT};

我想为每个实现一个 enum_to_string 函数和一个 string_to_enum 函数
将枚举转换为字符串没有问题,因为我可以重载相同的函数名。

std::string to_string(Race const& enum_value);
std::string to_string(Color const& enum_value);
std::string to_string(Direction const& enum_value);

但是,在转换为枚举时不能以相同的方式重载,因为只有返回类型会有所不同。 (我也不想,因为不同的枚举可能用相同的字符串表示。)


以下方法之一可以将字符串转换为枚举吗?

Race race = to_enum<Race>("elf");
Color color = to_enum<Color>("green");

std::string blah{"up"};
Direction dir{to_enum<Direction>(blah)};

或者可能:

Race race = to_enum(Race,"elf");
Color color = to_enum(Color,"green");

std::string blah{"up"};
Direction dir{to_enum(Direction,blah)};

C++ 能否支持其中一种或两种行为?


我试图避免像这样使用不同的函数名称:

Race to_race(std::string const& str);
Color to_color(std::string const& str);
Direction to_direction(std::string const& str);  

这是我能想到的最接近的东西,

template <typename T>struct to_enum{};
template <>
struct to_enum<Color>{

    static Color convert(std::string const& str){
        //match the string with a color enum, and return that color enum
        //(or like a default color enum if the string is garbage or something)
    }
};

然后你这样称呼它:

Color color = to_enum<Color>::convert("red");

我们可以摆脱皈依者吗?或者可能实施这个?

Color color = to_enum(Color,"red");

【问题讨论】:

  • 你可以避免Race race = to_enum&lt;Race&gt;("elf");中的冗余(例如)使用auto来避免重复类型:auto race = to_enum&lt;Race&gt;("elf");

标签: c++ class function c++11 enums


【解决方案1】:

使用函数模板和特化。这些是我认为函数专业化真正有帮助的罕见情况。

template<typename TEnum>
TEnum enum_cast(std::string const & s);

然后为每个枚举类型专门化它。我改了名字,所以你可以像 cast 一样阅读它:

Color color = enum_cast<Color>("blue");

或者parse也是个好名字。选择你认为好的任何东西。我个人会选择parseenum_cast

现在对于to_string,您可以定义重载(您已经拥有):

std::string to_string(Color c);
std::string to_string(Race r);
std::string to_string(Direction d);

不要对to_string 使用模板和特化。总的来说,重载确实很好。

【讨论】:

  • 天哪,我应该多睡点。
【解决方案2】:

然后你这样称呼它:

Color color = to_enum<Color>::convert("red");

我们可以摆脱皈依者吗?

当然!简单封装调用:

template <typename Enum>
Enum to_enum(std::string const& from) {
    return to_enum_helper<Enum>::convert(from);
}

to_enum_helper 是您之前定义的结构。 (编辑:或者您直接将功能专业化,如 Nawaz 的回答所示。)

或者,如果那是你的一杯茶,你可以使用重载而不是专业化:

Race to_enum_helper(std::string const& from, Race) {
    // Implement for enum class Race
}

Color to_enum_helper(std::string const& from, Color) {
    // Implement for enum class Color
}

template <typename Enum>
Enum to_enum(std::string const& from) {
    return to_enum_helper(from, Enum());
}

这里,to_enum_helper 的第二个参数只是用来区分不同的重载。

【讨论】:

    【解决方案3】:

    但是,在转换为枚举时不能以相同的方式重载,因为只有返回类型会不同。

    您可以重载转换运算符以编写仅返回类型不同的重载函数:

    #include <string>
    #include <iostream>
    
    enum Race {};
    enum Color {};
    
    struct to_one_of_them {
        operator Race  () const { std::cout << "to Race()\n"; return Race(); }
        operator Color () const { std::cout << "to Color()\n"; return Color(); }
    
        to_one_of_them() = delete;
    
    private:
        std::string str;
        to_one_of_them(std::string const &str) : str(str) {}
    
        friend to_one_of_them to_enum(std::string const&);
    };
    
    to_one_of_them to_enum(std::string const &str) {
        return {str};
    }
    
    
    int main () {
        Race  r = to_enum("meh");
        Color c = to_enum("meh");
    }
    

    但是,这种技术实际上没有被使用,因此不是 C++ 习语,因此可能不值得学习。

    【讨论】:

    • 不常见且成本高昂,因为您将原始字符串的副本复制到对象中只是为了调用转换。有更好的方法不会产生这种成本。
    • @DavidRodríguez-dribeas:当然,虽然这个解决方案符合用户的原始问题。我怀疑一般的编译器没有巧妙地内联它,但我自己仍然不会使用它。 (旁注:我已经提到它不常见)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-05
    • 1970-01-01
    • 2021-06-07
    • 1970-01-01
    相关资源
    最近更新 更多