2015-08-28 90 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