2

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

A) 仅处理形式参数的一种特定原始类型(int、double、float、...),而其他类型则抛出异常

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

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

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

4

3 回答 3

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. T 为 int 时的示例代码 1。没有错误。请忽略警告!
  2. T 为双倍时的示例代码2。现在,请参阅错误消息。请忽略警告!

同样,您可以为其他类型(double、char 等)编写模板。或者,更好的是,您可以简单地将所有这些合并到一个struct中,而是可以在单个模板中定义许多static const bool is_T_int = true;布尔值(针对每种类型),例如. 等等。做实验,你会学会的!

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


对于(B)部分,您从上面得到了想法。正确的?做实验!

于 2010-12-14T17:44:09.007 回答
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 );

(旁白:有关何时使用typename的说明,请参见此处

这是一次做两件事。

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

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

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

由于我们只提供了 forGateKeeper<int>而不是 for的定义,因此对GateKeeper<double>的调用可以foo(0)正常工作,并且会foo(0.0)因编译错误而失败。

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

template <> struct GateKeeper<double> { typedef int TYPE; }; // double now works.
于 2010-12-14T19:04:27.250 回答
1

您可以在 B 部分使用 boost::mpl ,但这是使用 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;
}
于 2010-12-14T18:03:47.790 回答