找到你要的答案

Q:How can I link a class to an enum?

Q:我怎么能联系到一个枚举类?

I have the following code:

#include <string>

enum class Hobbit {
    // typedef HobbitHelper helper;
    UNKNOWN = -1, Bilbo, Frodo, Samwise
};

struct HobbitHelper {
    static Hobbit decode(std::string const& s) {
        if (s == "Bilbo") {
            return Hobbit::Bilbo;
        }
        else if (s == "Frodo") {
            return Hobbit::Frodo;
        }
        else if (s == "Samwise") {
            return Hobbit::Samwise;
        }
        else {
            return Hobbit::UNKNOWN;
        }
    }
};

enum class Wizard {
    // typedef Apprentice helper;
    UNKNOWN = -1, Gandalf, Radagast, Saruman
};

struct Apprentice { // WizardHelper :)
    static Wizard decode(std::string const& s) {
        if (s == "Gandalf") {
            return Wizard::Gandalf;
        }
        else if (s == "Radagast") {
            return Wizard::Radagast;
        }
        else if (s == "Saruman") {
            return Wizard::Saruman;
        }
        else {
            return Wizard::UNKNOWN;
        }
    }
};

template <typename T>
T
decoder(std::string s)
{
    return ??::decode(s);
    // if the typedefs were allowed, I could use T::helper::decode()
}

int main()
{
    std::string s{ "Rincewind" };

    auto h = decoder<Hobbit>(s);
    auto w = decoder<Wizard>(s);
}

How can I arrange to call the appropriate helper class (HobbitHelper or Apprentice) in decoder? I can't declare a nested type inside the enum, as if it was a class. I also tried deriving the helper from the enum (since the helper itself has no data), but that isn't allowed either.

Any ideas?

我有以下代码:

#include <string>

enum class Hobbit {
    // typedef HobbitHelper helper;
    UNKNOWN = -1, Bilbo, Frodo, Samwise
};

struct HobbitHelper {
    static Hobbit decode(std::string const& s) {
        if (s == "Bilbo") {
            return Hobbit::Bilbo;
        }
        else if (s == "Frodo") {
            return Hobbit::Frodo;
        }
        else if (s == "Samwise") {
            return Hobbit::Samwise;
        }
        else {
            return Hobbit::UNKNOWN;
        }
    }
};

enum class Wizard {
    // typedef Apprentice helper;
    UNKNOWN = -1, Gandalf, Radagast, Saruman
};

struct Apprentice { // WizardHelper :)
    static Wizard decode(std::string const& s) {
        if (s == "Gandalf") {
            return Wizard::Gandalf;
        }
        else if (s == "Radagast") {
            return Wizard::Radagast;
        }
        else if (s == "Saruman") {
            return Wizard::Saruman;
        }
        else {
            return Wizard::UNKNOWN;
        }
    }
};

template <typename T>
T
decoder(std::string s)
{
    return ??::decode(s);
    // if the typedefs were allowed, I could use T::helper::decode()
}

int main()
{
    std::string s{ "Rincewind" };

    auto h = decoder<Hobbit>(s);
    auto w = decoder<Wizard>(s);
}

我该怎么安排给合适的辅助类(hobbithelper或学徒)解码器?我不能声明一个枚举嵌套类型里面,如果它是一个类。我也尝试从枚举得到帮手(因为帮手本身没有数据),但不允许。

有什么想法?

answer1: 回答1:

You can just have the helper type trait be external and templated on the enum type, with an explicit specialization for each enum:

template <typename T> struct type_is { using type = T; };

template <typename > struct helper;

template <> struct helper<Hobbit> : type_is<HobbitHelper> { };
template <> struct helper<Wizard> : type_is<Apprentice> { };

template <typename T>
using helper_t = typename helper<T>::type;

And then decode would just access that:

template <typename T>
T decoder(std::string s)
{
    return helper_t<T>::decode(s);
}

你有辅助型特点外,基于枚举类型就可以为每个枚举的显式特化:

template <typename T> struct type_is { using type = T; };

template <typename > struct helper;

template <> struct helper<Hobbit> : type_is<HobbitHelper> { };
template <> struct helper<Wizard> : type_is<Apprentice> { };

template <typename T>
using helper_t = typename helper<T>::type;

然后解码就可以访问:

template <typename T>
T decoder(std::string s)
{
    return helper_t<T>::decode(s);
}
answer2: 回答2:

The simplest way to do it is to use ADL. You can use a type tag to make the compiler look in the appropriate namespace.

Consider:

template<typename T> struct adl_tag {};
namespace MiddleEarth {
    enum class Hobbit {
        // typedef HobbitHelper helper;
        UNKNOWN = -1, Bilbo, Frodo, Samwise
    };
    Hobbit decode(std::string const& s, adl_tag<Hobbit>) {
        if (s == "Bilbo") {
            return Hobbit::Bilbo;
        }
        else if (s == "Frodo") {
            return Hobbit::Frodo;
        }
        else if (s == "Samwise") {
            return Hobbit::Samwise;
        }
        else {
            return Hobbit::UNKNOWN;
        }
    }
}
template<typename T> T decode(std::string s) {
    return decode(s, adl_tag<T>());
}

This is the solution employed by pretty much all C++ libraries- more or less. There's basically no additional effort involved. I didn't even have to mention Wizard.

这样做的最简单的方法是使用ADL。可以使用类型标记使编译器在适当的命名空间中查找。

考虑:

template<typename T> struct adl_tag {};
namespace MiddleEarth {
    enum class Hobbit {
        // typedef HobbitHelper helper;
        UNKNOWN = -1, Bilbo, Frodo, Samwise
    };
    Hobbit decode(std::string const& s, adl_tag<Hobbit>) {
        if (s == "Bilbo") {
            return Hobbit::Bilbo;
        }
        else if (s == "Frodo") {
            return Hobbit::Frodo;
        }
        else if (s == "Samwise") {
            return Hobbit::Samwise;
        }
        else {
            return Hobbit::UNKNOWN;
        }
    }
}
template<typename T> T decode(std::string s) {
    return decode(s, adl_tag<T>());
}

这是解决使用几乎所有的C++库,或多或少。基本上没有额外的努力。我甚至没有提到巫师。

answer3: 回答3:

My suggestion would be partial template specialization, although the answer from @Barry might be more like what you are looking for.

template <typename T>
T decoder(std::string s);

template<>
Hobbit decoder(std::string s)
{
    return HobbitHelper::decode(s);
}

template<>
Wizard decoder(std::string s)
{
    return Apprentice::decode(s);
}

我的建议是模板偏特化,虽然@巴里的回答可能更像是你正在寻找的。

template <typename T>
T decoder(std::string s);

template<>
Hobbit decoder(std::string s)
{
    return HobbitHelper::decode(s);
}

template<>
Wizard decoder(std::string s)
{
    return Apprentice::decode(s);
}
answer4: 回答4:

Aside helper problem there is better solution than cascade if:

static Hobbit decode(std::string const& s) {
    static std::unordered_map<std::strinng,Hobbit> choice {
        { "Bilbo", Hobbit::Bilbo },
        { "Frodo", Hobbit::Frodo },
        { "Samwise", Hobbit::Samwise }
    };
    auto f = choice.find( s );
    return f != choice.end() ? f->second : Hobbit::UNKNOWN;
}

除了辅助问题,有更好的解决方案比级联:

static Hobbit decode(std::string const& s) {
    static std::unordered_map<std::strinng,Hobbit> choice {
        { "Bilbo", Hobbit::Bilbo },
        { "Frodo", Hobbit::Frodo },
        { "Samwise", Hobbit::Samwise }
    };
    auto f = choice.find( s );
    return f != choice.end() ? f->second : Hobbit::UNKNOWN;
}
answer5: 回答5:

In the end I went with a slightly modified version of Barry's answer

template <typename T>
struct enumclass {
};

template<>
struct enumclass<Hobbit> {
    using helper = HobbitHelper;
};

template<>
struct enumclass<Wizard> {
    using helper = Apprentice;
};

because it lets me write the more mnemonic

template <typename T>
T
decoder(std::string s)
{
    return enumclass<T>::helper::decode(s);
}

All the specializations can be distributed (i.e. enumclass <Hobbit> is in hobbit.h; enumclass<Wizard> is in wizard.h). All these headers must include a small header with the unspecialized template.

最后我去了一个稍微修改的版本的巴里的回答

template <typename T>
struct enumclass {
};

template<>
struct enumclass<Hobbit> {
    using helper = HobbitHelper;
};

template<>
struct enumclass<Wizard> {
    using helper = Apprentice;
};

因为它让我写更多的记忆

template <typename T>
T
decoder(std::string s)
{
    return enumclass<T>::helper::decode(s);
}

所有专业可以分布(即enumclass <;霍比特人>;是霍比特人。H;enumclass <;向导& gt;是向导。H)。这些标题必须包括一个非模板小标题。

c++  templates  enums