限制模板功能,仅允许某些类型

时间:2015-08-28 09:07:49

标签: c++ templates c++11

这里说我有一个简单的模板功能,原则上可以接受所有类型:

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&lt;&lt; int(5);,将是一个编译错误,因为现在有两个可能的operator&lt;&lt;(std :: ostream,int)实现,一个是标准c ++,另一个是我的模板函数。

我想问一下,是否可以限制我的模板功能,以便它只接受我指定的几种类型?这是当我使用cout&lt;&lt;时告诉编译器忽略我的模板的方法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.
}

3 个答案:

答案 0 :(得分:5)

为此编写一个非常通用的解决方案很难。检查Tstd::vectorstd::array的任意类型std::array的问题是后者不是类,它们是类模板。更糟糕的是,std::vector是一个带有非类型模板参数的类模板,因此您甚至无法拥有一个同时包含std::arraynamespace 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; } 的参数包。

你可以通过在类型中明确地包装非类型参数来解决这个问题,但它变得丑陋,快速。

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

result = users.find_one({'_id': request._id, 'profiles.profile_name': 'harry'},{'_id': 0,'settings': 1})

请不要使用它,这太可怕了。

Live Demo

答案 1 :(得分: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的相同(您应该使用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> }

答案 2 :(得分:1)

使用SFINAE执行您要求的操作。

procedure TPairs<TKey, TValue>.SetValue(Key: TKey; const Value: TValue);
var
  i: Integer;
  Pair: TPair<TKey, TValue>;
begin
  i := IndexOfKey(Key);
  if i >= 0 then
    begin
      Pair := Items[i];
      Pair.Value := Value;
      Items[i] := Pair;
    end
  else
    begin
      Pair.Create(Key, Value);
      inherited Add(Pair);
    end;
end;

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