首页 > 解决方案 > C++ 访问尚未定义的模板参数成员

问题描述

用 c++11 做一些元编程。

我想构建一个 EnumWrapper,它可以用于 switch 语句和映射的关键字,但仍然具有类似对象的属性,如 Java Enumerations。

上课:

#include <string>
#include <vector>

template<typename T, int len>
class Enum
{
  private:
    typename T::Value value; // invalid use of incomplete Type Planet

  public:
    Enum() = delete;

    constexpr Enum( typename T::Value key ) : value( key ) // invalid use of incomplete Type Planet
    {
    }

    // Allow switch and comparisons.
    operator typename T::Value() const // invalid use of incomplete Type Planet
    {
        return value;
    }

    T& operator=( const T& other )
    {
        this->value = other.value;
        return *this;
    }

    inline typename T::Value getValue() // invalid use of incomplete Type Planet
    {
        return value;
    }

    explicit operator bool() = delete;

    Enum& operator=( const Enum& p )
    {
        this->value = p.value;
        return *this;
    }

    static inline int length()
    {
        return len;
    }

    static std::vector<T> values()
    {
        std::vector<T> values;
        values.reserve( length() );

        for ( int i = 0; i < length(); i++ )
        {
            values.push_back( (typename T::Value)i ); // This is fine for some reason
        }

        return values;
    }

    static T getMember( std::string name )
    {
        for ( T p : values() )
        {
            if ( p.name() == name )
            {
                return p;
            }
        }
    }
};


class Planet : Enum<Planet, 3>
{
  public:
    enum Value
    {
        MERCURY,
        VENUS,
        EARTH
    }
    
    std::string name()
    {
         switch(this->getValue())
         {
             case MERCURY:
                 return "Mercury";
             case EARTH:
                 return "Mercury";
             case VENUS:
                 return "Mercury";
         }
    }
};

编译器给出了几个“不完整类型星球的无效使用”消息。

将这些类合并为一个类时,代码会编译并按预期工作。但是可重用性和可维护性并没有通过合并的方法给出。

有人可以帮我解决此错误消息吗?

如果需要或想要更多信息,请告诉我。

编辑: 我的预期用途是这样的:

int main()
{
    Planet planet        = Planet::EARTH;
    Planet invalidPlanet = Planet( (Planet::Value)5 );

    // attributes with enum working
    std::cout << "My Planet is named " << planet.name() << std::endl;
    Planet venus = Planet::VENUS;
    std::cout << "Gravity of Venus is " << venus.gravitation() << std::endl;

    // Switch working
    switch ( planet )
    {
    case Planet::VENUS:
        std::cout << "Venus found." << std::endl;
        break;
    case Planet::EARTH:
        std::cout << "Earth found." << std::endl;
        break;
    default:
        std::cout << "Nothing found." << std::endl;
        break;
    }

    // simple comparison
    bool isEarth = planet == Planet::EARTH; // true
    bool isVenus = planet == Planet::VENUS; // false

    std::cout << "Planet is earth: " << ( isEarth ? "true" : "false" ) << std::endl;
    std::cout << "Planet is venus: " << ( isVenus ? "true" : "false" ) << std::endl;

    // works in Map
    std::map<Planet, std::string> myMap;
    myMap[ planet ] = "MyEarth";
    
    std::cout << "chosen planet: " << myMap[ Planet::MERCURY ] << std::endl;
    std::cout << "chosen planet: " << myMap[ Planet::EARTH ] << std::endl;

    // Map is not bound to object but bound to enum integer
    Planet fakeEarth = Planet::EARTH;
    std::cout << "chosen planet: " << myMap[ fakeEarth ] << std::endl;

    return 1;
}

标签: c++c++11templatesinheritanceenums

解决方案


问题是一个类在它的右大括号之前被认为是不完整的,除了它的成员函数的主体。您的Planet类取决于其Enum可用的基类的定义。但是,基类依赖于T::Value成员函数体的外部,因此您有一个场景,类Planet最终取决于Planet可用的类的定义。

您可以enumPlanet类中分离出您的并将其作为单独的模板参数传递给基类,如下所示:

template<class ENUM, typename T, int len>
class Enum
{
  private:
   ENUM value;
//...
};

enum Planets
{
    MERCURY,
    VENUS,
    EARTH
};

class Planet : public Enum<Planets, Planet, 3>
{ /*...*/}

现场演示


另一件要担心的是,它Enum::getMember可能会退出而不返回任何东西。我建议您将其修改为像这样抛出:

static T getMember( std::string name )
{
    for ( T p : values() )
    {
        if ( p.name() == name )
        {
            return p;
        }
    }
    throw std::invalid_argument("Could not find" + name);
}

其他小事:

  • 您不小心使用了"Mercury"所有三个枚举值的名称
  • 偏爱enum class( enum class Planets)
  • 更喜欢static_cast代替 C 风格的强制转换 ( static_cast<ENUM>(i)))
  • 您认为枚举值将从零开始并增加 1 的假设是脆弱的
  • 手动传递枚举中值的数量的需要很脆弱
  • 奇怪的是,一个有效的实例enum有一个这样的方法getValue,它将返回你实际持有的枚举,但也混合了类似的方法valueslength用于获取有关所持有的枚举类型的信息;这可能会令人困惑
  • 希望当我们在 C++ 中获得元类时,这一切都会变得容易得多。

更新的演示


推荐阅读