关于c ++:限制模板功能

Restrict Template Function

我在http://codepad.org/ko8vvcdf上编写了一个使用模板函数的示例程序。

如何检索模板函数以仅使用数字?(int、double等)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <vector>
#include <iostream>

using namespace std;

    template <typename T>
T sum(vector<T>& a)
{
    T result = 0;
    int size = a.size();
    for(int i = 0; i < size; i++)
    {
        result += a[i];
    }

    return result;
}

int main()
{
    vector<int> int_values;
    int_values.push_back(2);
    int_values.push_back(3);
    cout <<"Integer:" << sum(int_values) << endl;

    vector<double> double_values;
    double_values.push_back(1.5);
    double_values.push_back(2.1);
    cout <<"Double:" << sum(double_values);

    return 0;
}


这可以通过使用sFIAE来实现,并且通过使用Boost或C++ 11的帮助器来变得更容易。

促进:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <vector>
#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/is_arithmetic.hpp>

template<typename T>
    typename boost::enable_if<typename boost::is_arithmetic<T>::type, T>::type
        sum(const std::vector<T>& vec)
{
  typedef typename std::vector<T>::size_type size_type;
  T result;
  size_type size = vec.size();
  for(size_type i = 0; i < size; i++)
  {
    result += vec[i];
  }

  return result;
}

C++ 11:

1
2
3
4
5
6
7
8
9
10
11
12
#include <vector>
#include <type_traits>

template<typename T>
    typename std::enable_if<std::is_arithmetic<T>::value, T>::type
        sum(const std::vector<T>& vec)
{
  T result;
  for (auto item : vec)
    result += item;
  return result;
}


你可以这样做:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
template <class T>
class NumbersOnly
{
private:
    void ValidateType( int    &i ) const {}
    void ValidateType( long   &l ) const {}
    void ValidateType( double &d ) const {}
    void ValidateType( float  &f ) const {}

public:
    NumbersOnly()
    {
       T valid;
       ValidateType( valid );
    };
};

如果尝试仅创建一个没有validateType重载的数字,则会出现错误:

1
2
NumbersOnly<int> justFine;
NumbersOnly<SomeClass> noDeal;


限制模板的唯一方法是使其使用您想要的类型,而其他类型没有的类型。

所以,使用int构造,使用+和+=,调用一个复制构造函数,等等。

任何具有所有这些特性的类型都可以使用您的函数——所以,如果我创建一个具有这些特性的新类型,您的函数就可以使用它——这很好,不是吗?

如果您想更多地限制它,请使用更多只为您想要的类型定义的函数。

实现这一点的另一种方法是创建一个特性模板——类似于这样

1
2
3
4
5
6
template<class T>
SumTraits
{
public:
  const static bool canUseSum = false;
}

然后将它专门用于您想要确定的类:

1
2
3
4
5
6
template<>
class SumTraits<int>
{
  public:
    const static bool canUseSum = true;
};

然后在代码中,您可以编写

1
2
3
if (!SumTraits<T>::canUseSum) {
   // throw something here
}

编辑:正如注释中所提到的,可以使用boost-static-assert将其设置为编译时检查,而不是运行时检查。


你就是这样做的。

例如,对double的模板专门化进行注释。它不允许您使用double作为参数调用该函数。诀窍是,如果您尝试使用不属于IsNumber的专门化的类型调用sum,那么将调用泛型实现,并且该实现使某些内容不被允许(调用私有构造函数)。

除非将IsNumber类重命名为听起来像错误消息的类,否则错误消息是不直观的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <vector>
#include <iostream>

using namespace std;

template<class T> struct IsNumber{
 private:
 IsNumber(){}
 };

 template<> struct IsNumber<float>{
   IsNumber(){};
 };

 template<> struct IsNumber<double>{
   IsNumber(){};
 };

 template<> struct IsNumber<int>{
   IsNumber(){};
 };

template <typename T>
T sum(vector<T>& a)
{
 IsNumber<T> test;
 T result = 0;
 int size = a.size();
 for(int i = 0; i < size; i++)
 {
  result += a[i];
 }

 return result;
}




int main()
{
 vector<int> int_values;
 int_values.push_back(2);
 int_values.push_back(3);
 cout <<"Integer:" << sum(int_values) << endl;

 vector<double> double_values;
 double_values.push_back(1.5);
 double_values.push_back(2.1);
 cout <<"Double:" << sum(double_values);

 return 0;
}

事实上,没有必要让它更严格。请看一下字符串版本(使用ChrisJester Young建议的默认构造函数样式)。

同样要注意溢出-您可能需要更大的类型来包含中间结果(或输出结果)。欢迎来到元编程领域,那么:)


您可以查看类型特性(使用Boost、等待C++ 0x或创建自己的)。

我在谷歌上找到了以下信息:http://artins.org/ben/programming/mactechgrp-artin-cpp-type-traits.pdf


在这种情况下,为什么要限制类型?模板允许"静态鸭式键入",因此在这种情况下,您的sum函数所允许的任何内容都应该被允许。具体来说,T所需的唯一操作是添加0的赋值和初始化,因此支持这两个操作的任何类型都可以工作。这就是模板的美妙之处。

(如果您将初始化器更改为T result = T();或类似,那么它也适用于数字和字符串。)