【问题标题】:Polymorphic Enums多态枚举
【发布时间】:2012-03-30 23:55:14
【问题描述】:

多态枚举?

在 C++ 中,我们经常使用多态性来让旧代码处理新代码 代码——例如,只要我们子类化 a 期望的接口 函数,我们可以传入新类并期望它正常工作 使用在新类存在之前编写的代码。 不幸的是,使用枚举,你不能真正做到这一点,即使有 是你偶尔想要的。 (例如,如果你是 管理程序的设置,并将所有设置存储为 枚举值,那么最好有一个枚举,settings_t,来自 你所有的其他枚举都继承了,这样你就可以存储每个 设置列表中的新枚举。请注意,由于列表包含 不同类型的值,不能使用模板。)

如果您需要这种行为,则必须将枚举存储为 整数,然后使用类型转换检索它们以分配 对感兴趣的设置具有特定的价值。你甚至不会得到 dynamic_cast 的好处是帮助您确保演员表是 安全——你必须依赖这样一个事实,即不能存在不正确的值 存储在列表中。

我引用的是C++ programming tutorial

谁能更深入地解释一下多态枚举是如何工作的? 如果我有模板?

【问题讨论】:

  • 我认为从引用的文本中可以清楚地看出,多态 enums 不起作用,至少在最简单的意义上是这样。 enum 基本上是具有基础整数存储类型的命名常量。在您想要使用多态 enum 的任何地方,使用模板可能会有更优雅的解决方案。
  • 多态枚举没有多大意义,如果你认为你需要它们,那么你的设计很可能是粗制滥造的。如果你真的需要它们,那么 Boost.Variant 应该可以工作。
  • @Chad 你能通过一个小代码 sn-p 示例帮助我更好地理解吗?
  • polographic enums 毫无意义。
  • @Matteo,请参阅下面的答案,无论如何它都不是“短”:)

标签: c++ enums polymorphism


【解决方案1】:

简单地说,enum 只是一个命名的常量值,例如:

enum Settings
{
   setting_number_0,
   setting_number_1,
   setting_number_2,      
};

在上面的示例中,setting_number_X 只是值 X 的命名常量,因为枚举值从 0 开始并单调递增。

然后保留这些,在某些类型的容器中提供整数的基本存储类型,但仍然可以在某种程度上是类型安全的。

std::vector<Setting> app_settings;

// this works
app_settings.push_back(setting_number_0);

// this is a compile time failure, even though the underlying storage
// type for Setting is an integral value.  This keeps you from adding
// invalid settings types to your container (like 13 here)
app_settings.push_back(13);

// but you also cannot (directly) add valid setting values (like 1)
// as an integral, this is also a compile time failure.
app_settings.push_back(1);

现在,假设您想添加额外的特定设置类型并将它们全部保存在一个容器中。

enum DisplaySettings
{
   // ...
};

enum EngineSettings
{
   // ...
};

现在,如果您想将所有设置保存在一个容器中,则不能安全地。您可以将所有整数值存储在 std::vector&lt;int&gt; 或类似容器中,但这会导致您无法确定哪些整数类型属于哪些设置枚举。此外,由于类型不同,您不能将它们存储在单个类型安全的容器中。

解决此问题的正确方法是将设置的功能存储在容器中,如下所示:

#include <vector>
#include <iostream>

// This is our "base class" type so we can store lots of 
// different setting types in our container
class setting_action
{
public:
   // we enable the setting by calling our function
   void enable_setting()
   {
      setting_function_(this);
   }

protected:
   // This is a function pointer, and we're using it to get some
   // compile time polymorphism
   typedef void (*setting_function_type)(setting_action* setting);

   // these can only be constructed by derived types, and the derived
   // type will provide the polymorhpic behavior by means of the 
   // above function pointer and based on the derived type's handler
   setting_action(setting_function_type func)
      : setting_function_(func)
   {
   }

public:
   ~setting_action()
   {
   }

private:
   setting_function_type setting_function_;
};

// This is the derived type, and where most of the magic
// happens.  This is templated on our actual setting type
// that we define below    
template <class Setting>
class templated_setting_action
   : public setting_action
{
public:
   templated_setting_action(Setting setting)
      : setting_action(&templated_setting_action::enable_setting)
      , setting_(setting)
   {
   }

   // This function catches the "enable_setting" call from
   // our base class, and directs it to the handler functor
   // object that we've defined
   static void enable_setting(setting_action* base)
   {
      templated_setting_action<Setting>* local_this = 
         static_cast<templated_setting_action<Setting>*>(base);

      local_this->setting_();
   }

private:
   Setting setting_;
};

// this is just a shorthand way of creating the specialized types
template <class T>
setting_action* create_specialized_setting_action(T type)
{
   return
      new templated_setting_action<T>(type);
}

// Our actual settings:
// this one displays the user name    
struct display_user_name
{
   void operator()()
   {
      std::cout << "Chad.\n";
   }
};

// this one displays a short welcome message    
struct display_welcome_message
{
   void operator()()
   {
      std::cout << "Ahh, the magic of templates.  Welcome!\n";
   }
};

// now, we can have one container for ALL our application settings

std::vector<setting_action*> app_settings;

int main()
{
   // now we can add our settings to the container...
   app_settings.push_back(create_specialized_setting_action(display_user_name()));
   app_settings.push_back(create_specialized_setting_action(display_welcome_message()));

   // and individually enable them
   app_settings[0]->enable_setting();
   app_settings[1]->enable_setting();

   // also, need to delete each setting to avoid leaking the memory
   // left as an exercise for the reader :)
   return 0;
}

【讨论】:

    猜你喜欢
    • 2017-08-08
    • 2014-06-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-02-15
    • 2015-03-31
    • 2015-04-26
    相关资源
    最近更新 更多