如何为相同类型的typedef提供模板专业化? [英] How can I provide template specializations for typedefs of the same type?

查看:50
本文介绍了如何为相同类型的typedef提供模板专业化?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

第三方SDK定义了几个typedef,例如:

A 3rd party SDK defines several typedefs, e.g.:

typedef unsigned char SDK_BYTE
typedef double SDK_DOUBLE
typedef unsigned char SDK_BOOLEAN

它还定义了SdkVariant的变体类型:

It also defines a variant type SdkVariant:

class SdkVariant
{
public:
    enum SdkType { SdkByte, SdkDouble, SdkBoolean };
    bool toByte(SDK_BYTE&);
    bool toDouble(SDK_DOUBLE&);
    bool toBool(SDK_BOOLEAN&);
    SdkType type();
};

从这样的变量中检索值看起来像这样(假定,我们知道所包含值的类型):

Retrieving a value from such a variant looks like this (presuming, we know the type of the contained value):

SdkVariant variant(foobar());
double value;
bool res = variant.toDouble(value);
if (!res)
    diePainfully();
else
    doSomethingWith(value);

这很冗长,因此我想提供一个variant_cast-function-class,可以执行该值检索和错误处理:

This is quite verbose and therefore I want to provide a variant_cast-function-class that can perform the value retrieval and error handling:

// general interface:
template<class T>
class variant_cast
{
public:
    T operator()(const SdkVariant& variant);
};

// template specializations:
template<>
SDK_DOUBLE variant_cast<SDK_DOUBLE>::operator()(const SdkVariant& variant)
{
    SDK_DOUBLE value;
    bool res = variant.toDouble(value);
    if (!res)
        diePainfully();
    return value;
}

template<>
SDK_BYTE variant_cast<SDK_BYTE>::operator()(const SdkVariant& variant)
{
    SDK_BYTE value;
    bool res = variant.toByte(value);
    if (!res)
        diePainfully();
    return value;
}

template<>
SDK_BOOLEAN variant_cast<SDK_BOOLEAN>::operator()(const SdkVariant& variant)
{
    SDK_BOOLEAN value;
    bool res = variant.toByte(value);
    if (!res)
        diePainfully();
    return value;
}

这不会编译(C2995:已定义功能模板),因为SDK_BYTE和SDK_BOOLEAN是相同的类型(无符号字符)。现在,我的想法是让预处理器检查SDK_BYTE和SDK_BOOLEAN是否相同,如果是,则为两者定义单个模板专用化。如果它们不同,则应使用上面两个独立的专业。像这样:

This does not compile (C2995: Function template already defined), because SDK_BYTE and SDK_BOOLEAN are the same types (unsigned char). My idea is now to let the preprocessor check that SDK_BYTE and SDK_BOOLEAN are the same and if so, define a single template specialization for both. If they're different, it should use the two separate specializations from above. Like this:

#if SDK_BYTE == SDK_BOOLEAN
template<>
SDK_BYTE variant_cast<SDK_BYTE>::operator()(const SdkVariant& variant)
{
    SDK_BYTE value;
    bool res;
    if (variant.type() == SdkByte)
        res = variant.toByte(value);
    else
        res = variant.toBool(value);
    if (!res)
        diePainfully();
    return value;
}
#else
    // code from above
#endif

上述代码的问题是,预处理器似乎无法解析两个typedef。有没有办法在预处理期间比较两个typedef(正确)?如果不是,是否有办法阻止编译器解析typedef,以便它可以接受SDK_BYTE和SDK_BOOLEAN的两种不同的模板专业化?如果没有,我仍然可以提供单个模板专业化功能,并在SDK_BYTE和SDK_BOOLEAN不相等的情况下使用BOOST_STATIC_ASSERT使编译器失败,但是有没有更好的方法来解决我的问题?

The problem with the above code is, that it appears to be impossible for the preprocessor to resolve the two typedefs. Is there a way to compare two typedefs (correctly) during preprocessing? If not, is there a way to hinder the compiler from resolving the typedefs, so that it would accept two different template specializations for SDK_BYTE and SDK_BOOLEAN? If not, I can still provide the single template specialization and use BOOST_STATIC_ASSERT to make the compiler fail if SDK_BYTE and SDK_BOOLEAN are unequal, but is there a better way to solve my problem?

推荐答案

如果您选择使用C ++ 11,则以下代码说明了使用 std :: enable_if std :: is_same

If C++11 is an option for you, here is some code illustrating a possible solution using std::enable_if and std::is_same:

#include <iostream>
#include <type_traits>

struct SdkVariant
{
};

typedef int   type1;
typedef float type2;

template <typename T, typename Enable=void>
class variant_cast
{
public:
  /* Default implementation of the converter. This is undefined, but
     you can define it to throw an exception instead. */
  T operator()(const SdkVariant &v);
};

/* Conversion for type1. */
template <typename T>
class variant_cast<T,typename std::enable_if<std::is_same<T,type1>::value>::type>
{
public:
  type1 operator()(const SdkVariant &v)
  {
    return type1 { 0 };
  }
};

/* Conversion for type2, IF type2 != type1. Otherwise this
   specialization will never be used. */
template <typename T>
class variant_cast<T,typename std::enable_if<
         std::is_same<T,type2>::value
      && !std::is_same<type1,type2>::value>::type>
{
 public:
  type2 operator()(const SdkVariant &v)
  {
    return type2 { 1 };
  }
};

int main()
{
  variant_cast<type1> vc1;
  variant_cast<type2> vc2;
  std::cout << vc1({}) << std::endl;
  std::cout << vc2({}) << std::endl;
  return 0;
}

一些注意事项:


  1. 除了定义该库定义的各种类型外,我只定义了 type1 type2

  2. 我已经定义了一个空的 SdkVariant 结构作为虚拟对象

  3. 由于该虚拟对象为空,因此我的转换并没有真正转换任何内容。转换为 type1 时仅输出一个常数(值0),转换为 type2 (如果 type2 实际上与 type1 不同)。

  4. 要测试它是否满足您的要求,可以将 type2 的定义替换为

  1. Instead of the various types you get defined by that library, I have only defined type1 and type2
  2. I have defined an empty SdkVariant struct as a dummy
  3. Because that dummy is empty, my conversion does not really convert anything. It just outputs a constant (value 0) when converting to type1, and a constant (value 1) when converting to type2 (if type2 is actually different from type1).
  4. To test whether it does what you need, you may replace the definition of type2 with

typedef int type2;

因此它与 type1 。它仍然可以编译,并且不会有任何与双重定义相关的错误。

so it is identical with the definition for type1. It will still compile, and there will be no error related to any double definition.






关于使用 std :: enable_if 以及局部和显式模板专业化的说明


Remark about the use of std::enable_if and partial vs. explicit template specializations

type1 的转换器声明为

template <typename T>
variant_cast<T,typename std::enable_if<std::is_same<T,type1>::value>::type>

这意味着它是为 T任何类型定义的 type1 相同。相反,我们可以使用显式的专业化

which means it is defined for any type T that is the same as type1. Instead, we could have used an explicit specialization

template <>
variant_cast<type1>

这要简单得多,也可以

我没有这样做的唯一原因是 type2 的情况下将无法正常工作,因为对于 type2 ,我们必须检查它是否与 type1 相同,即我们必须使用 std :: enable_if

The only reason I didn't do that is that in the case of type2 it won't work, because for type2 we must check whether it is the same as type1, i.e. we must use std::enable_if:

template <>
class variant_cast<type2,
   typename std::enable_if<!std::is_same<type1,type2>::value>::type>

不幸的是,您不能使用 std :: enable_if 在显式专业化中,因为显式专业化不是模板–它是真实的数据类型,编译器必须对其进行处理。如果 type1 type2 相同,则:

Unfortunately, you can't use std::enable_if in an explicit specialization, because an explicit specialization is not a template – it's a real data type, and the compiler must process it. If type1 and type2 are identical, this:

typename std::enable_if<!std::is_same<type1,type2>::value>::type

不存在,因为 std :: enable_if 的工作方式。因此编译失败,因为它无法实例化此数据类型。

does not exist, because of the way std::enable_if works. So the compilation fails because it can't instantiate this data type.

通过为任何 T $ c>与 type2 相同,我们避免显式实例化 type2 ,因此我们不这样做不要强迫编译器对其进行处理。如果 std :: enable_if< ...> :: type ,它只会处理 type2 的模板专业化确实存在。否则,它只会忽略它,而这正是我们想要的。

By defining the converter for any type T that is the same as type2 we avoid the explicit instantiation for type2, hence we don't force the compiler to process it. It will only process the template specialization for type2 if the std::enable_if<...>::type actually exists. Otherwise it will simply ignore it, which is exactly what we want.

同样,对于 type1 (以及任何其他 type3 type4 等),显式实例化将起作用。

Again, in the case of type1 (and for any further type3, type4 etc.) an explicit instantiation will work.

我认为值得指出的是,为与 T 定义模板专业化 > type 是一个一般适用的技巧,因为您出于正式原因不能使用显式专业化,因此您使用了部分专业化,但实际上只想将其绑定到这一类型。例如,成员模板不能被显式实例化,除非其封装模板也被显式实例化。结合使用 std :: enable_if std :: is_same 可能也有帮助。

I think it's worthwhile pointing out that defining a template specialization for any type T that is the same as some type type is a trick that is generally applicable whenever you can't use an explicit specialization for formal reasons, so you use a partial specialization, but you really want to bind it to this one type only. For instance, a member template cannot be explicitly instantiated unless its enclosing template is explicitly instantiated, too. Using a combination of std::enable_if and std::is_same probably helps there, too.

这篇关于如何为相同类型的typedef提供模板专业化?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆