2015-08-28 59 views
4

在这里说,我有一个简单的模板功能,原则上可以接受所有种类型:限制模板功能,只允许某些类型

template <class Type> 
std::ostream& operator<< (std::ostream& stream, const Type subject) { 
stream << "whatever, derived from subject\n"; 
return stream; } 

我只是想用这个模板来清点几类,说std :: vector和boost :: array对象。然而,无论何时我将cout用于其他类型甚至基本类型,例如std :: cout < < int(5);,将是一个编译错误,因为现在有两种可能的运算符< <(std :: ostream,int)的实现,一个是标准C++,另一个是我的模板指定的功能。

我想问一下,是否可以限制我的模板函数,以便它只接受我指定的几种类型?这是如何告诉编译器忽略我的模板当我使用cout < < int(5)。提前致谢。

更清楚,这就是我想做的事:

template <class Type> 
std::ostream& operator<< (std::ostream& stream, const Type subject) { 
if (Type == TypeA or TypeB or TypeC) //use this template and do these {...}; 
else //ignore this template, and use operator<< provided in standard c++ library. 
} 
+1

可能重复的http://计算器。COM /问题/ 874298/C的模板,也就是说,接受只,某些类型) –

+0

的问题是,我想包括类型,如性病::阵列,性病::阵列,性病::阵列, ......这就是一个无穷级数不同类型的....不知道是否有一种方法可以做到这一点.. –

+0

我仍然无法找到答案,如何防止实例化编译器从我的模板函数,当我使用cout << int(5)。当断言失败时,static_assert或BOOST_STATIC_ASSERT将生成一个编译错误,而不是忽略我的模板。 –

回答

2

您可以限制超载是这样的:

template <class T> 
std::ostream& my_private_ostream(std::ostream& stream, const T& data) 
    { <your implementation> } 

template <class T, class A> 
std::ostream& operator<< (std::ostream& stream, const std::vector<T,A>& data) 
    { return my_private_ostream(stream,data); } 

同为std::array S(你应该标记您的问题C++ 11):

template <class T, size_t N> 
std::ostream& operator<< (std::ostream& stream, const std::array<T,N>& data) 
    { return my_private_ostream(stream,data); } 

或者,对于看起来更像您的编辑的解决方案,您可以使用C++ 11 enable_if,尽管我对他们有个人厌恶,因为他们倾向于使代码难以阅读和维护。所以我强烈推荐以前的解决方案。

// Vector type predicate 
template <class T> 
struct is_vector: std::false_type {}; 

template <class T, class A> 
struct is_vector< std::vector<T,A> >: std::true_type {}; 

// Array type predicate 
template <class T> 
struct is_array: std::false_type {}; 

template <class T, size_t N> 
struct is_array< std::array<T,N> >: std::true_type {}; 

// The overload with the syntax you want 
template <class Indexable> 
typename std::enable_if< 
    is_vector<Indexable>::value || is_array<Indexable>::value, 
    std::ostream& 
>::type 
operator<< (std::ostream& stream, const Indexable& data) 
    { <your implementation> } 
+0

我想编写一个模板来处理几种不同的类型。模板的好处是我不必写出每个不同的实例。 –

+0

这就是C++的工作原理,你可以想要任何你喜欢的东西,但这并不意味着语言必须允许它。你想要什么是可能的,但是你必须为每个_class_实现**一个**重载(即一个用于'vector's,一个用于'array's等等)。如果你只需要一个逻辑实现,你可以创建你自己的函数my_output_stream,并从重载中调用它。事实上,有人提出了一个名为Concept Lite的C++ 14,允许指定模板类型的谓词。它被拒绝了,可能在未来的C++版本中。 – Sheljohn

+1

我想你可以* *做到这一点与C++ 11,但有必要处理正常的类型和模板类型将使它很丑陋。 – TartanLlama

0

使用SFINAE来做你在问什么。

template<typename...> 
struct is_vector: std::false_type{}; 

template<typename T, typename Alloc> 
struct is_vector<std::vector<T, Alloc>>: std::true_type{}; 

template<typename...> 
struct is_array: std::false_type{}; 

template<typename T, std::size_t Size> 
struct is_array<std::array<T, Size>>: std::true_type{}; 

template<typename T> 
struct is_my_ostream_type{ 
    enum { 
     value = is_vector<T>::value || is_array<T>::value 
    }; 
}; 

template< 
     typename T, 
     typename = typename std::enable_if<is_my_ostream_type<T>::value>::type 
> 
std::ostream &operator <<(std::ostream &lhs, const T &rhs){ 
    lhs << "is my special ostream overload"; 
    return lhs; 
} 

但是你可能最终只会为每种类型写一个重载,而不是这样做。

3

为此编写一个真正通用的解决方案很困难。检查任意类型Tstd::vectorstd::array的问题是后者不是类,它们是类模板。更糟糕的是,std::array是一个带有非模板参数的类模板,所以你甚至不能拥有一个参数包,它可以容纳std::vectorstd::array

你可以通过在类型中显式包装非类型参数来解决这个问题,但它会变得很难看,速度很快。

这是我提出的一个解决方案,它将默认支持任何没有非模板参数的类或模板类。通过添加包装类型来将非类型参数映射到类型参数,可以支持具有非类型模板参数的模板类。

namespace detail{ 
    //checks if two types are instantiations of the same class template 
    template<typename T, typename U> struct same_template_as: std::false_type {}; 
    template<template<typename...> class X, typename... Y, typename... Z> 
    struct same_template_as<X<Y...>, X<Z...>> : std::true_type {}; 

    //this will be used to wrap template classes with non-type args 
    template <typename T> 
    struct wrapImpl { using type = T; }; 

    //a wrapper for std::array 
    template <typename T, typename N> struct ArrayWrapper; 
    template <typename T, std::size_t N> 
    struct ArrayWrapper<T, std::integral_constant<std::size_t, N>> { 
     using type = std::array<T,N>; 
    }; 

    //maps std::array to the ArrayWrapper 
    template <typename T, std::size_t N> 
    struct wrapImpl<std::array<T,N>> { 
     using type = ArrayWrapper<T,std::integral_constant<std::size_t,N>>; 
    }; 

    template <typename T> 
    using wrap = typename wrapImpl<typename std::decay<T>::type>::type; 

    //checks if a type is the same is one of the types in TList, 
    //or is an instantiation of the same template as a type in TempTList 
    //default case for when this is false 
    template <typename T, typename TList, typename TempTList> 
    struct one_of { 
     using type = std::false_type; 
    }; 

    //still types in the first list to check, but the first one doesn't match 
    template <typename T, typename First, typename... Ts, typename TempTList> 
    struct one_of<T, std::tuple<First, Ts...>, TempTList> { 
     using type = typename one_of<T, std::tuple<Ts...>, TempTList>::type; 
    }; 

    //type matches one in first list, return true 
    template <typename T, typename... Ts, typename TempTList> 
    struct one_of<T, std::tuple<T, Ts...>, TempTList> { 
     using type = std::true_type; 
    }; 

    //first list finished, check second list 
    template <typename T, typename FirstTemp, typename... TempTs> 
    struct one_of<T, std::tuple<>, std::tuple<FirstTemp, TempTs...>> { 
     //check if T is an instantiation of the same template as first in the list 
     using type = 
      typename std::conditional<same_template_as<wrap<FirstTemp>, T>::value, 
       std::true_type, 
       typename one_of<T, std::tuple<>, std::tuple<TempTs...>>::type>::type; 
    }; 
} 

//top level usage 
template <typename T, typename... Ts> 
using one_of = typename detail::one_of<detail::wrap<T>,Ts...>::type; 

struct Foo{}; 
struct Bar{}; 

template <class Type> 
auto operator<< (std::ostream& stream, const Type subject) 
    //is Type one of Foo or Bar, or an instantiation of std::vector or std::array 
    -> typename std::enable_if< 
       one_of<Type, std::tuple<Foo,Bar>, std::tuple<std::vector<int>,std::array<int,0>> 
     >::value, std::ostream&>::type 
{ 
    stream << "whatever, derived from subject\n"; 
    return stream; 
} 

请不要使用这个,这太可怕了。的[C++模板,仅接受某些类型](

Live Demo

+1

创建一个怪物感觉如何? :)我认为可以通过查看'::','<>'和'''的密度来预测读取C++的局部心理疼痛的数量。 – Sheljohn