【问题标题】:Can I force initialization for all possible enum values of an enum class class/function template?我可以强制初始化枚举类类/函数模板的所有可能的枚举值吗?
【发布时间】:2015-08-03 13:26:17
【问题描述】:

我有一个 template class 使用 enum class 作为模板参数,在头文件中定义:

// MyClass.h
enum class MyEnum {FOO, BAR};

template<MyEnum T>class MyClass {
  void doStuff();
  // ...
};

我希望将成员函数的实际实现放在单独的源文件中。我知道,在那种情况下,我必须为每种情况强制执行模板的初始化:

//MyClass.cpp
template<MyEnum T>void MyClass<T>::doStuff() {
  // ...
}
// Implementations of other functions

template class MyClass<MyEnum::FOO>;
template class MyClass<MyEnum::BAR>;

通常,我知道我需要具有 all 可能值的类 enum 无论如何,所以我想告诉编译器它实际上应该为每个可能值构建模板类没有明确提及每种可能性

换句话说:我想用一些自动化代码替换示例中的最后两行。

这有可能吗?

我也想用模板函数而不是模板类做同样的事情。

【问题讨论】:

  • 你的意思是template class MyClass&lt;MyEnum::FOO&gt;;
  • 我认为可以编写一个递归模板,在每次迭代中用下一个值实例化一个模板,直到达到最后一个枚举值。但是,除非您在标头中扩展每个模板实例,否则编译器无论如何都会在您想使用它们的每个翻译单元中实例化它们...
  • 谢谢,@Barry。相应地更正了我的问题。
  • @Melkon:谢谢你的建议,但我想我至少需要让我的枚举可迭代,我发现这是可能的,但不是很优雅。不过,我会考虑的。
  • 您也可以将每个值作为可变参数模板传递...在这种情况下,您应该明确地编写它。像这样:实例化;这可能比你一个一个实例化它们要好一些。可能还不够好。

标签: c++ templates c++11 enums


【解决方案1】:

让编译器知道所有可能的特化的通常方法是在同一个文件中实现它们。我想不出一种理智的方法来让编译器知道模板类是专门针对域中的每个值的。

如果你有无数个,你可以考虑使用X macro pattern。像这样将所有案例隔离在一个头文件中(比如MyEnum.h):

#ifndef HANDLE_ENUM_CASE(e)
#define HANDLE_ENUM_CASE(e)
#endif

HANDLE_ENUM_CASE(FOO)
HANDLE_ENUM_CASE(BAR)
// add more HANDLE_ENUM_CASE(...) expressions for every other case you have

#undef HANDLE_ENUM_CASE

在您的源文件中,您可以:

enum class MyEnum {
#define HANDLE_ENUM_CASE(e) e,
#include "MyEnum.h"
}

// (class declaration here)

#define HANDLE_ENUM_CASE(e) template class MyClass<MyEnum::e>;
#include "MyEnum.h"

【讨论】:

  • 不是MyEnum.h nuking HANDLE_ENUM_CASE 的忠实粉丝,但 X-macros 绝对是要走的路。
  • 感谢您的解决方法,@zneak。尽管如此,我还是接受了您的回答,而不是您的陈述:“我想不出一种理智的方式[...]”。可能这是我问题的唯一正确答案。
【解决方案2】:

在过去,枚举只是整数值的自定义名称,这很简单:我们只是添加了一个 MAX 值作为最后一个枚举,并仅使用 for (int i=0; i&lt;MAX; i++) 进行迭代。

如果您使用简单的enum MyEnum { FOO, BAR };,您至少可以将 MyEnum 转换为 int,(但不是相反,这太容易了)。但是 enum class MyEnum { FOO, BAR }; 转换为 int 也没有定义。

所以唯一的 C++11 枚举方式是 ... zneak 提出的宏方式 ... sniff :-(

如果你不喜欢(我不喜欢),你必须忘记enum class 并使用纯整数,或者至少作为typedef。不仅如此,非类型模板参数必须是常量表达式,因此您还应该忘记非类型模板构造。

这会导致:

namespace MyEnum {

    typedef int MyEnum_t;
    enum MyEnum : MyEnum_t  { FOO, BAR, MAX };
}

class MyClass {
    MyEnum::MyEnum_t T;
public:
    MyClass(MyEnum::MyEnum_t t): T(t) {}
    void doStuff() {
        std::cout << T << std::endl;
    }
};

您可能应该声明一个复制构造函数,以确保您只在兼容类型之间进行复制,因为编译器无法再控制它。但至少你可以这样做:

for(MyEnum::MyEnum_t e=0; e<MyEnum::MAX; e++) {
    MyClass obj(e);
    obj.doStuff();
}

顺便说一句,MyClass 将不再是模板,您可以放心地将成员函数的实际实现放在单独的源文件中...

【讨论】:

  • 我认为这不是预期的答案。我只是为了展示 zneak 提出的宏的可能替代方案及其后果。
  • 谢谢@Serge_Ballesta,我注意到在某些情况下我实际上根本不需要使用模板,并且可以简化为函数参数(就像你在构造函数中所做的那样)。但是,我也有返回 type 实际上依赖于模板参数的函数,因此您的解决方案无法在那里工作。
  • @user2296653:所以我无法想象除了宏之外的任何东西 :-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-04-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多