如何根据返回类型选择重载

时间:2016-09-12 13:41:03

标签: c++

在我的项目中,枚举值通常需要写出日志文件或作为字符串保存。所以,我已经提供了ToString和StringToEnum函数,如下例所示:

namespace Mine
{
    enum class Color { red, green, blue };

    inline std::wstring ToString(Color c)
    {
        switch (c)
        {
        case Color::red: return L"red";
        case Color::green: return L"green";
        case Color::blue: return L"blue";
        default: THROW_MACRO("Unexpected value[{}] for enum[{}]", c, L"Color");
        }
    }

    inline void StringToEnum(const std::wstring& inEnumValueName, Color& out)
    {
        if (inEnumValueName == L"red")
        {
            out = Color::red;
        }
        else if (inEnumValueName == L"green")
        {
            out = Color::green;
        }
        else if (inEnumValueName == L"blue")
        {
            out = Color::blue;
        }
        else
        {
            THROW_MACRO("Unexpected value[{}] for enum[{}]", inEnumValueName, L"Color");
        }
    }
}

当我使用StringToEnum时,我最终写了:

Color c;
StrintToEnum(L"red", c);
// use c

我真的希望能够在一行上声明和初始化并写下:

auto c = ToEnum<Mine::Color>(L"red);

我像这样定义了ToEnum并将其放在要包含的标题中:

namespace CommonCode
{
    template<class T>
    T ToEnum(const std::wstring& enumValueName)
    {
        T value;
        StringToEnum(enumValueName, value);
        return value;
    }
}

问题是,ToEnum无法编译,因为尚未定义相关的StringToEnum函数。

是否有一种有用的方法可以对此进行编码,或者每当我想要从字符串声明和初始化枚举值时,我是否需要编写两行代码?

我试图专门化ToEnum,但是我遇到了这个问题,我需要关闭命名空间我定义枚举,打开CommonCode命名空间并添加到那个,然后再回到原始命名空间。这是很多打字,看起来很难看。

(我使用的是Visual Studio 2015 Update 3,因此任何使用它编译的解决方案都是首选。)

3 个答案:

答案 0 :(得分:1)

StringToEnum实际上应该返回Enum。原样,充其量使用有点令人讨厌。自MSVC incorrectly implements两阶段模板查找以来,您的函数模板解决方案失败。因此,我们只需将其他内容传递给我们可以用来确定类型的StringToEnum。尽可能清楚地说明这个&#34;其他东西&#34;的目的是什么?是的,我们将它变成自己的类型:

template <class T> struct tag { };

在您的原始示例中:

inline Color StringToEnum(const std::wstring& inEnumValueName, tag<Color> )
{
    if (inEnumValueName == L"red")
    {
        return Color::red;
    }
    else if (inEnumValueName == L"green")
    {
        return Color::green;
    }
    // etc.
}

现在,只要您想将字符串转换为枚举E,那就是:

auto e = StringToEnum(str, tag<E>{});

可以通过泛型过载缩短:

template <class T>
inline T StringToEnum(const std::wstring& name) {
    return StringToEnum(name, tag<T>{} );
}

auto e = StringToEnum<E>(str);

答案 1 :(得分:0)

您有不同的名称空间。你的模板声明几乎是正确的,但它需要知道什么是StringToEnum。您只需在使用函数时放置命名空间:

namespace CommonCode {
    template<class T>
    T ToEnum(const std::wstring& enumValueName) {
        T value;

        // Notice the namespace here
        Mine::StringToEnum(enumValueName, value);
        return value;
    }
}

<击> 我建议直接直接实例化每个函数:

template<class T>
T toEnum(const std::string&);

template<>
Color toEnum<Color>(const std::string&) {
    if (inEnumValueName == L"red") {
        out = Color::red;
    }

    // ...
}

它将删除样板文件,让您按照自己的意愿使用函数,并以正确的方式实现该语法。

答案 2 :(得分:0)

您可以使用模板专业化:

#include <string>

enum class Fail { err };
enum class Color { red,blue,green};

//generic template: causes error in compilation
template<typename T> 
T enumToString(std::string s)
{
    static_assert(sizeof(T) == -1,"no overload for enum");
}

//specialization for Color
template<>
Color enumToString(std::string s)
{
    if(s == "red") 
        return Color::red;
    else if(s == "blue")
        return Color::blue;
    return Color::green;
}

int main()
{

    auto c = enumToString<Color>("blue");
//  auto e = enumToString<Fail>("e"); //this will cause a compiler error
    return static_cast<int>(c);
}

只需为您需要的每个枚举专门化模板。我还添加了一个默认实现,如果没有专门化,它将失败。