针对特定类型的部分模板专业化,c++

发布于 2024-10-07 17:53:38 字数 324 浏览 0 评论 0原文

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

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

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

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

一些代码示例会很有帮助(无需 C++ boost 库)。感谢您的帮助。

Using partial specialization of templates I would like to create a function/method:

A) processing only one specific primitive type (int, double, float,...) of the formal parameter and for other types throwing exception

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

B) processing more non-primitive types (user defined types) of the formal parameter and for other types throwing exception...

Some code examples would be helpful (without c++ boost library). Thanks for your help.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

南街九尾狐 2024-10-14 17:53:38

这就是您的问题的解决方案(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. 示例
    代码1

    当 T 为 int 时。没有错误。请忽略
    不过警告!
  2. 示例
    代码2

    当 T 为双倍时。现在,查看错误
    留言也。请忽略
    不过警告!

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

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


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

This is the solution of your problem (part 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
}

See these sample code.

  1. sample
    code1

    when T is int. No error. Please ignore the
    warning though!
  2. sample
    code2

    when T is double. Now, see the error
    message also. Please ignore the
    warning though!

Similarly, you can write templates for other types, (double, char, whatever). Or, even better, you can simply merge all those into just one struct, and instead can define many booleans (for each type) in a single template, like static const bool is_T_int = true;. Etc. Do experiments, you'll learn!

But then, I wonder if you want your function to handle just one type, then why define template to start with?


For part(B), you get the idea from the above. Right? Do experiments!

初与友歌 2024-10-14 17:53:38

您可以在此处使用 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;
}

you can use boost::mpl here for part B but this is use 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;
}

EDIT

with compile time check everything is simpler:

#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;
}
网白 2024-10-14 17:53:38

正如 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)。这是因为它无法推导出 foo(T) 的所有模板参数。

foo(T) 的主体将调用转发给真正的函数:(

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

旁白:请参阅 此处了解何时使用typename

这会同时执行两件事。

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

第二种是使用 GateKeeper 的成员作为其他类型。类型 GateKeeper 必须完整并具有该成员。正是这个检查使我们能够指定我们想要允许哪些类型以及不允许哪些类型:

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

因为我们只提供了 GateKeeper 的定义,而不是 GateKeeper的定义;,对 foo(0) 的调用工作正常,foo(0.0) 失败并出现编译错误。

要允许 double ,我们只需要为其添加显式特化:

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

As mentioned in the comment by Alexender C., are you sure a compile error wouldn't be more suitable?

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
}

A partial specialization can be used to allow any specialization of a given template:

// 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
}

For each type that you wish to support you add a new specialization.

UPDATE: What is happening:

Note: I've changed the template parameter names from the original version to make things a bit clearer.

When the compiler sees a call to foo(x) the only function that it can correctly specialize is foo<T>(T). This is because it cannot deduce all the template parameters for foo<S,T>(T).

The body of foo<T>(T) forwards the call to the real function:

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

(ASIDE: see here for description of when to use typename)

This is doing 2 things at once.

The first is to provide the 2 template parameters (S and T) which are required to call the other function.

The second is to use a member of GateKeeper<T> as this other type. The type GateKeeper<T> must be complete and have that member. It is this check that allows us to specify which types we want to allow and which we don't:

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

As we have only provided a definition for GateKeeper<int> and not for GateKeeper<double>, the call to foo(0) works correctly, and foo(0.0) fails with a compile error.

To allow double, we just need to add an explicit specialization for it:

template <> struct GateKeeper<double> { typedef int TYPE; }; // double now works.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文