我可以使用类型特征重载函数吗?

时间:2013-12-20 08:17:42

标签: c++ templates c++11 template-meta-programming typetraits

让我们说,我有六种类型,它们各自都属于一个概念类别 这是一个显示这一点的图表:

Types A, B, and C wrapped inside a box called "Type Category 1" and types D, E, and F wrapped inside a box called "Type Category 2"


或许也许是一个更具体的例子: Apple, Orange and Banana are all Fruit.  Carrot, Onion, and Cabbage are all Vegetables


我想写两个能处理所有6种类型的函数 “类别1”中的类型以某种方式处理,“类别2”中的类型以不同的方式处理。

让我们进入代码。 首先,我将创建六种类型。

//Category 1 Types
class Type_A{};
class Type_B{};
class Type_C{};

//Category 2 Types
class Type_D{};
class Type_E{};
class Type_F{};

接下来,我将创建两个类型特征,以便在编译时可以发现类型的类别。

/* Build The Category 1 Type Trait */

//Type_A Type Trait
template <typename T>
struct Is_Type_A {
  static const bool value = false;
};
template <>
struct Is_Type_A<Type_A> {
  static const bool value = true;
};

//Type_B Type Trait
template <typename T>
struct Is_Type_B {
  static const bool value = false;
};
template <>
struct Is_Type_B<Type_B> {
  static const bool value = true;
};

//Type_C Type Trait
template <typename T>
struct Is_Type_C {
  static const bool value = false;
};
template <>
struct Is_Type_C<Type_C> {
  static const bool value = true;
};

//Category 1 Type Trait
template <typename T>
struct Is_Type_From_Category_1 {
  static const bool value = Is_Type_A<T>::value || Is_Type_B<T>::value || Is_Type_C<T>::value;
};

/* Build The Category 2 Type Trait */

//Type_D Type Trait
template <typename T>
struct Is_Type_D {
  static const bool value = false;
};
template <>
struct Is_Type_D<Type_D> {
  static const bool value = true;
};

//Type_E Type Trait
template <typename T>
struct Is_Type_E {
  static const bool value = false;
};
template <>
struct Is_Type_E<Type_E> {
  static const bool value = true;
};

//Type_F Type Trait
template <typename T>
struct Is_Type_F {
  static const bool value = false;
};
template <>
struct Is_Type_F<Type_F> {
  static const bool value = true;
};

//Category 1 Type Trait
template <typename T>
struct Is_Type_From_Category_2 {
  static const bool value = Is_Type_D<T>::value || Is_Type_E<T>::value || Is_Type_F<T>::value;
};

现在我有两种类型特征来区分六种类型中的每一种类型,我想写两个函数。一个函数将接受来自类别1的所有内容,而另一个函数将接受来自类别2的所有内容。有没有办法在不创建某种调度功能的情况下执行此操作?我能找到一种只有两种功能的方法;每个类别一个?


编辑:我曾试图像这样使用enable_if,但这样的尝试会导致编译错误。

//Handle all types from Category 1
template<class T ,class = typename std::enable_if<Is_Type_From_Category_1<T>::value>::type >
void function(T t){
    //do category 1 stuff to the type
    return;
}

//Handle all types from Category 2
template<class T ,class = typename std::enable_if<Is_Type_From_Category_2<T>::value>::type >
void function(T t){
    //do category 2 stuff to the type
    return;
}

编辑2:我已经尝试过链接中提供的代码,但这不是关于是否调用该函数的是或否决定。给定两种类型特征,我可以调用哪个函数。这将是重新定义错误。

//Handle all types from Category 2
template<class T, class dummy = typename std::enable_if< Is_Type_From_Category_1<T>::value, void>::type>
void function(T t){
    //do category 1 stuff to the type
    return;
}
//Handle all types from Category 2
template<class T, class dummy = typename std::enable_if< Is_Type_From_Category_2<T>::value, void>::type>
void function(T t){
    //do category 2 stuff to the type
    return;
}

4 个答案:

答案 0 :(得分:11)

不允许两个函数签名仅因模板参数的默认值而不同。如果您明确调用function< int, void >

会发生什么

enable_if的常用用法是函数返回类型。

//Handle all types from Category 1
template<class T >
typename std::enable_if<Is_Type_From_Category_1<T>::value>::type
function(T t){
    //do category 1 stuff to the type
    return;
}

//Handle all types from Category 2
template<class T >
typename std::enable_if<Is_Type_From_Category_2<T>::value>::type
function(T t){
    //do category 2 stuff to the type
    return;
}

答案 1 :(得分:10)

我认为使用标签发送会比SFINAE更容易。

template<class T>
struct Category;

template<>
struct Category<Type_A> : std::integral_constant<int, 1> {};
template<>
struct Category<Type_B> : std::integral_constant<int, 1> {};
template<>
struct Category<Type_C> : std::integral_constant<int, 1> {};

template<>
struct Category<Type_D> : std::integral_constant<int, 2> {};
template<>
struct Category<Type_E> : std::integral_constant<int, 2> {};
template<>
struct Category<Type_F> : std::integral_constant<int, 2> {};

template<class T>
void foo(std::integral_constant<int, 1>, T x)
{
    // Category 1 types.
}

template<class T>
void foo(std::integral_constant<int, 2>, T x)
{
    // Category 2 types.
}

template<class T>
void foo(T x)
{
    foo(Category<T>(), x);
}

答案 2 :(得分:3)

作为通过“traits”选择类别的替代方法,您还可以考虑CRTP(类型以类别为基础):

template<class Derived> class category1 {};
template<class Derived> class category2 {};

class A1: public category1<A1> { ..... };
class A2: public category2<A2> { ..... };
class B1: public category1<B1> { ..... };
class B2: public category2<B2> { ..... };

template<class T>void funcion_on1(category1<T>& st)
{
   T& t = static_cast<T&>(st);
   .....
}

template<class T>void funcion_on1(category2<T>& st)
{
   T& t = static_cast<T&>(st);
   .....
}

优点是具有污染较少的命名空间。

答案 3 :(得分:3)

我从R. Martinho Fernandes学到了以下技巧。下面显示的代码是为了说明问题的基础,但你应该参考这个blog post来获得完整的技巧来使它漂亮。

您已经提到过,由于签名相同,您遇到了问题。诀窍是使类型不同。

您的第二种方法已接近,但我们无法使用void作为std::enable_if<>的结果类型。

请注意,以下代码无法编译,并且为void指定std::enable_if<>不会更改任何内容,因为默认情况下为void

#include <iostream>

class A {};
class B {};

template <
    typename T, 
    typename = typename std::enable_if<std::is_same<T, A>::value>::type>
void F(T) {
  std::cout << "A" << std::endl;
}

template <
    typename T, 
    typename = typename std::enable_if<std::is_same<T, B>::value>::type>
void F(T) {
  std::cout << "B" << std::endl;
}

int main() {
  F(A{});
  F(B{});
}

正如您所描述的那样,原因是签名是相同的。让我们区分它们。

#include <iostream>

class A {};
class B {};

template <
    typename T,
    typename std::enable_if<std::is_same<T, A>::value, int>::type = 0>
void F(T) {
  std::cout << "A" << std::endl;
}

template <
    typename T, 
    typename std::enable_if<std::is_same<T, B>::value, int>::type = 0>
void F(T) {
  std::cout << "B" << std::endl;
}

int main() {
  F(A{});
  F(B{});
}

打印:

A
B

我们现在区分了两个函数之间的类型,因为它不是第二个模板参数,而是一个类型,它现在是int

这种方法比在返回类型中使用std::enable_if<>更可取,例如,因为构造函数没有返回类型,所以模式不适用于那些。

注意:std::is_same<>与单个类一起使用以简化条件。