C ++基于枚举模板参数的成员函数的多个定义

时间:2017-06-11 13:28:31

标签: c++

考虑以下代码:

template<typename T, SomeEnum mode> struct TC{

    T data;

    //...

    void doStuff();
};

“doStuff”可以根据为模板设置的枚举值有多个定义吗?

TC<int, SomeEnum::MODE_1> tc1;    tc.doStuff(); //do some stuff
TC<int, SomeEnum::MODE_2> tc2;    tc.doStuff(); //do some other stuff

(我不是指保存“模式”并在其上创建一个分支,但实际上是多个定义。)

2 个答案:

答案 0 :(得分:4)

你可以做标签调度。只需为枚举的每个打包值提供重载:

template<typename T, SomeEnum mode> struct TC{

    T data;

    //...
    template<SomeEnum v>
    using tag_type = std::integral_constant<SomeEnum, v>;

    void reallyDoStuff(tag_type<SomeEnum::MODE_1>);
    void reallyDoStuff(tag_type<SomeEnum::MODE_2>);

    void doStuff() { reallyDoStuff(tag_type<mode>{}); }
};

由于类模板的成员函数除非被使用,否则不会被实例化,因此您只需为reallyDoStuff的每个实例实例化TC(正确的一个)的一个定义

如果有疑问,请选择函数模板重载到专门化。它通常是最好的选择。

答案 1 :(得分:0)

通常,这是模板专业化的用途。如果你不知道什么是模板专业,你需要先阅读你的C ++书籍,然后再阅读我的答案。

这里唯一的绊脚石是单个类方法不能专门化,整个类必须是专用的。但是有一些常见的方法,例如以下内容。

将您的成员函数定义为辅助模板类的函数调用包装器,如下所示:

template<typename T, SomeEnum mode> void TC::doStuff()
{
    doStuff_helper<T, mode>::doStuff(*this);
}

那是你真正的doStuff()。实际代码进入辅助类。 定义帮助程序类模板如下(当然,您需要正确使用前向声明和其他此类杂项):

template<typename T, SomeEnum mode> class doStuff_helper {
public:

   static void doStuff(TC<T, mode> &me)
   {
      // ...
   }
};

原始类方法所做的一切现在都可以在这里完成,但有一些明显的区别。这不是原始类的实际方法了。因此,您可以使用this引用而不是原始me。并且因为这不是实际的类方法,所以访问私有或受保护的类成员会遇到常见问题。但这些是根据自身优点轻松解决的细微问题。关键是你现在能做的就是专注于整个事情:

template<typename T> class doStuff_helper<T, MODE_VALUE> {
public:

   static void doStuff(TC<T, MODE_VALUE> &me)
   {
      // ...
   }
};

doStuff()现在可以完全不同了。这是将不允许的类方法专业化转换为普通的,花园种类,类专业化的一般方法。

对这种常用方法有进一步的改进,经常使用。一个这样的改进就是将这个因素考虑在内doStuff()本身只不过是对me的包装和方法调用,一般和专用版本在原始模板类中调用不同的方法。 / p>

一旦你用一张纸和一支铅笔弄清楚这里发生了什么,你会发现它最终做的是将一个调用转到原来的doStuff()类方法来调用两个不同的类方法(通常为private),具体取决于原始模板类的参数。这两个不同的类方法基本上是您最初想要拥有的两个不同版本的doStuff(),只使用适当的方法,具体取决于模板参数。