[英]restrict a template function, to only allow certain types
Here say I have a simple template function that in principle can accept all kind of types: 这里说我有一个简单的模板功能,原则上可以接受所有类型:
template <class Type>
std::ostream& operator<< (std::ostream& stream, const Type subject) {
stream << "whatever, derived from subject\n";
return stream; }
I only want to use this template to cout a few types, say std::vector and boost::array objects. 我只想用这个模板来讨论几种类型,比如std :: vector和boost :: array对象。 However whenever I use cout to other types even elementary types, eg std::cout << int(5);, will be a compilation error, because there are two possible implementations of operator<<(std::ostream, int) now, one is in standard c++, the other specified by my template function.
但是每当我使用cout到其他类型甚至基本类型时,例如std :: cout << int(5);,都会出现编译错误,因为现在有两种可能的operator <<(std :: ostream,int)实现一个是标准的c ++,另一个是我的模板函数。
I would like to ask, is it possible to restrict my template function, so that it only accepts a few types specified by me? 我想问一下,是否可以限制我的模板功能,以便它只接受我指定的几种类型? That is how to tell the compiler to ignore my template when i use cout << int(5).
这是当我使用cout << int(5)时告诉编译器忽略我的模板的方法。 Thanks in advance.
提前致谢。
To be more clear, this is what I want to do: 更清楚,这就是我想要做的:
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.
}
Writing a really generic solution for this is hard. 为此编写一个非常通用的解决方案很难。 The problem with checking an arbitrary type
T
against std::vector
or std::array
is that the latter are not classes, they are class templates. 检查
std::vector
或std::array
的任意类型T
的问题是后者不是类,它们是类模板。 Even worse, std::array
is a class template with a non-type template parameter, so you can't even have a parameter pack which will hold both std::vector
and std::array
. 更糟糕的是,
std::array
是一个带有非类型模板参数的类模板,因此你甚至不能拥有一个包含std::vector
和std::array
的参数包。
You can get around this somewhat by explicitly wrapping non-type parameters up in types, but it gets ugly, fast. 你可以通过在类型中明确地包装非类型参数来解决这个问题,但它会变得丑陋,快速。
Here is a solution I came up with that will support any class or template class with no non-type template parameters by default. 这是我提出的一个解决方案,它将支持默认情况下没有非类型模板参数的任何类或模板类。 Template classes with non-type template parameters can be supported by adding a wrapper type to map non-type parameters to type parameters.
通过添加包装类型以将非类型参数映射到类型参数,可以支持具有非类型模板参数的模板类。
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;
}
Please don't use this, it's horrible. 请不要使用它,这太可怕了。
You can restrict your overload like this: 您可以像这样限制您的过载:
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); }
Same for std::array
s (you should tag your question with c++11): 对于
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); }
Alternatively, for a solution that looks a bit more like your edit, you could use C++11 enable_if
, although I have a personal aversion to them as they tend to make the code difficult to read and maintain. 或者,对于看起来更像编辑的解决方案,您可以使用C ++ 11
enable_if
,尽管我对他们有个人厌恶,因为它们往往会使代码难以阅读和维护。 So I strongly recommend the previous solution. 所以我强烈推荐以前的解决方案。
// 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> }
Use SFINAE to do what you're asking. 使用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;
}
But you're probably going to end up just writing an overload for every type rather than doing this. 但是你可能最终只是为每种类型写一个重载而不是这样做。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.