关于模板类专业化的范例

时间:2016-01-30 16:45:21

标签: c++ templates c++11 template-specialization

我目前正在编写一个模板类,用于存档(或序列化)和从二进制格式中取消存档数据。首先,我试图关闭我将使用的模式。我主要倾向于使用模板,因为unarchivers没有方法重载的输入类型。例如,以下示例为OK:

Archiver ar;
int i;

archive(ar, i);

但它的对手不是:

Unarchiver unar;
int i;

i = unarchive(unar);

我想避免使用函数的名称,例如unarchive_int,因为在使用模板时会很麻烦。说:

template <class T> class SomeClass
{
public:

   void doSomething()
   {
      // Somewhere
      T value = unarchive(unar);
   }
};

这会使事情变得混乱,因此我真的使用模板,而前一个表达式将是T value = unarchive<T>(ar);。如果第一个或唯一的参数始终是archiver和unarchiver对象,那么编写全局函数似乎也很愚蠢(可以说);模板类似乎是有序的:

template <class T> class Archiver
{
public:

    void archive(T obj);
};

这样可行,但归档方法始终复制其输入对象。对于POD数据类型,这是可以的,但不是那么多类。解决方案似乎很明显,而是使用void archive(const T & obj)中的const引用,但现在通过引用传递整数,浮点数和其他POD似乎也很愚蠢。虽然我对这个解决方案感到满意,但我还是试着进一步让对象做出区分。我的第一种方法是std::enable_if,默认情况下假设一个副本(对于所有非类成员)并提供一个类专门化,其中archive方法通过引用获取其输入。它不起作用。这是代码:

template <class T, class E = void>
class Archiver
{
public:

    // By default, obj is passed by copy
    void archive(T obj);
};

template <class T>
class Archiver<T, typename std::enable_if<std::is_class<T>::value && !std::is_pod<T>::value>::value>
{
public:

    // I would expect this to be used instead if is_class<T> && !is_pod<T>
    void archive(const T & obj);
};

问题是编译器根本看不到第二个声明,这里有证明:

template <> void Archiver<std::uint8_t>::archive(uint8_t obj);
template <> void Archiver<std::string>::archive(const std::string & obj);

前者编译得很好,但后者给出了:

  

&#39;档案&#39;的外线声明不符合任何声明   'Archiver<std::__1::basic_string<char>, void>'

另一方面,如果我得到std::string而不是副本,如果编译得很好。我想我知道为什么会发生这种情况,编译器会选择第一个模板,因为它对两个声明都是通用的,但是如何让它选择更专业的版本呢?

2 个答案:

答案 0 :(得分:5)

您需要std::enable_if<...>::type std::enable_if<...>::value

Here's a full demo

#include <type_traits>
#include <cstdint>
#include <string>

template <class T, class E = void>
struct Archiver {
    void archive(T obj);
};

template <class T>
struct Archiver<T, typename std::enable_if<std::is_class<T>::value && !std::is_pod<T>::value>::type>
{
    void archive(const T & obj);
};

template <> void Archiver<std::uint8_t>::archive(std::uint8_t obj);
template <> void Archiver<std::string>::archive(const std::string & obj);

答案 1 :(得分:1)

IIUC,问题归结为如何定义针对调用函数优化的通用模板类型。

为此,您可以考虑boost::call_traits,尤其是param_type

template<typename T>
void foo(typename boost::call_traits<T>::param_type t);