特定类型的部分模板特化,c ++

时间:2010-12-14 17:38:14

标签: c++ templates specialization partial-specialization

使用模板的部分特化我想创建一个函数/方法:

A)只处理形式参数的一个特定基本类型(int,double,float,...)和其他类型抛出异常

template <class T>
T min ( Point <T> p )
{
    /*if (T == int) continue;
    else throw exception*/
}

B)处理形式参数的更多非原始类型(用户定义类型)以及抛出异常的其他类型......

一些代码示例会有所帮助(没有c ++ boost库)。谢谢你的帮助。

3 个答案:

答案 0 :(得分:7)

这是您的问题的解决方案(A部分)。

template<bool b> struct compile_time_assert;

template<> 
struct compile_time_assert<true> 
{};

template<class T> 
struct is_int 
{ 
     static const bool value = false; 
};
template<> 
struct is_int<int> 
{ 
     static const bool value = true; 
};

template <class T>
T min ( Point <T> p )
{
    /* 
     since you can check error at compile time, there is no reason to 
     raise exception (which is at runtime) if T is not int! 
     the following assert will not compile if T is not int.
     not only that, you can even see the error message "_error_T_is_not_int" 
     if T is not int;
    */

    compile_time_assert<is_int<T>::value> _error_T_is_not_int;

    //your code
}

请参阅以下示例代码。

  1. sample code1 当T为int时。没错。请忽略 警告虽然!
  2. sample code2 当T是双倍的时候。现在,看看错误 消息也。请忽略 警告虽然!
  3. 同样,您可以为其他类型编写模板,(double,char,等等)。或者,更好的是,您只需将所有这些合并到一个struct中,而是可以在单个模板中定义多个布尔值(针对每种类型),例如static const bool is_T_int = true;。等等做实验,你会学到的!

    但是,我想知道你是否希望你的函数只处理一种类型,那么为什么要定义模板呢?


    对于(B)部分,您可以从上面得到这个想法。对?做实验!

答案 1 :(得分:1)

你可以在这里使用boost :: mpl作为B部分,但这是使用boost:(

#include <boost/mpl/at.hpp>
#include <boost/mpl/map.hpp>
#include <boost/mpl/bool.hpp>

using namespace boost;

typedef mpl::map<
    mpl::pair<int, mpl::true_>,
    mpl::pair<double, mpl::false_>,
    mpl::pair<float, mpl::false_>
> TAllowedTypesForMin;

template <class T>
T min (T p)
{
    const bool allowed = mpl::at<TAllowedTypesForMin, T>::type::value;
    if (allowed)
    {

    }

    return p;
}

修改

使用编译时检查一切都更简单:

#include <boost/mpl/set.hpp>
#include <boost/mpl/assert.hpp>

using namespace boost;

template<class T> struct Point {};
typedef mpl::set<int, double, float> TAllowedTypesForMin;

template <class T>
T min (Point<T> p)
{
    typedef mpl::has_key<TAllowedTypesForMin, T> allowedType;
    BOOST_MPL_ASSERT_MSG( allowedType::value, NOT_SUPPORTED_TYPE, () );

// do something

    return T();
}

int main() 
{
        min(Point<long>());
        return 0;
}

答案 2 :(得分:1)

正如Alexender C.的评论中提到的,你确定编译错误不太适合吗?

template <typename T> struct GateKeeper;

// Func we want to call
template <typename S, typename T> void foo (T t);

// Warpper that checks the type and forwards the call
template <typename T> inline void foo (T t)
{
  //
  // This call will fail for unless a specialization of
  // GateKeeper for `T` is defined with a member TYPE
  foo< typename GateKeeper<T>::TYPE, T > ( t );
}

//
// This declaration "allows" the type int.
template <> struct GateKeeper<int> { typedef int TYPE; };

void bar ()
{
  foo (0);   // Compiles
  foo (0.0); // Causes error in wrapping foo
}

部分特化可用于允许给定模板的任何特化:

// Some template type
template <typename T>
class TmplType
{
};

// This allows for specializations of TmplType
template <typename T> struct GateKeeper< TmplType<T> > { typedef int TYPE; };

void bar ()
{
  TmplType<char> tt;
  foo (tt);   // Compiles
}

对于您希望支持的每种类型,请添加新的专业化。

更新:发生了什么:

注意:我已经从原始版本更改了模板参数名称,以使事情更清晰。

当编译器看到对foo(x)的调用时,它可以正确专门化的唯一函数是foo<T>(T)。这是因为它无法推导出foo<S,T>(T)的所有模板参数。

foo<T>(T)的正文将调用转发给真正的函数:

foo< typename GateKeeper<T>::TYPE, T > ( t );

(ASIDE:有关何时使用 typename 的说明,请参阅here

这是一次做两件事。

第一个是提供调用另一个函数所需的2个模板参数(S和T)。

第二种是使用GateKeeper<T>的成员作为另一种类型。类型GateKeeper<T>必须完整并具有该成员。正是这个检查允许我们指定我们想要允许的类型和不允许的类型:

template <typename T> struct GateKeeper;                  // Incomplete
template <> struct GateKeeper<int> { typedef int TYPE; }; // Complete

由于我们仅为GateKeeper<int>而非GateKeeper<double>提供了定义,因此对foo(0)的调用正常,foo(0.0)因编译错误而失败。

要允许double,我们只需要为它添加一个明确的专业化:

template <> struct GateKeeper<double> { typedef int TYPE; }; // double now works.