为类型 T 的容器专门化模板
Specializing a template for a container of type T
假设我有一个模板设置来对类型执行某些操作,例如...
template<typename T>
class SimpleTemplate
{
private:
T m_obj;
public:
void operator()() { m_obj.DoSomething(); }
};
而且我想以同样的方式处理我有一个类型为 T 的集合的情况。我目前有一个类似矢量的模板设置...
template<typename T>
class SimpleTemplate<std::vector<T>>
{
private:
std::vector<T> m_collection;
public:
void operator()()
{
for (auto&& obj : m_collection) obj.DoSomething();
}
};
现在我也想支持集合,unordered_sets等等。我可以为每个集合编写一个模板,但我觉得这对于模板来说应该是一个完美的工作,只是我不知道应该如何编写,或者即使它可以?
我可以做类似 template<typename C<T>>
的事情吗?
Now I want to also support sets, unordered_sets and so on. I could write a template for each collection but I feel like this should be a perfect job for a template, only I can't figure out how it should be written, or even if it can be
也许你可以使用模板模板参数
template <template <typename...> class C, typename... Ts>
class SimpleTemplate<C<Ts...>>
{
private:
C<Ts...> m_collection;
public:
void operator()()
{
for (auto&& obj : m_collection) obj.DoSomething();
}
};
这应该截取std::(unordered_)(multi)set
、std::vector
、std::deque
等
不幸的是没有拦截std::array
,因为它的第二个模板参数是一个值,而不是类型。
正如 Geoffroy 所提到的,您可以使用特征来检测 T
是否可以迭代。然后你用它来 select 正确的专业化。
因此,从 Jarod42 here:
所展示的 "is_iterable" 特征开始
// Code by Jarod42 (
#include <iterator>
#include <type_traits>
namespace detail
{
// To allow ADL with custom begin/end
using std::begin;
using std::end;
template <typename T>
auto is_iterable_impl(int)
-> decltype (
begin(std::declval<T&>()) != end(std::declval<T&>()), // begin/end and operator !=
void(), // Handle evil operator ,
++std::declval<decltype(begin(std::declval<T&>()))&>(), // operator ++
void(*begin(std::declval<T&>())), // operator*
std::true_type{});
template <typename T>
std::false_type is_iterable_impl(...);
}
template <typename T>
using is_iterable = decltype(detail::is_iterable_impl<T>(0));
这为您提供了一个 is_iterable<T>
特征,它继承自 std::true_type
或 std::false_type
。现在将其与 SFINAE 一起使用来创建两个专业:
template <class T, bool = is_iterable<T>::value>
class SimpleTemplate;
template <class T>
class SimpleTemplate<T, false> {
private:
T m_obj;
public:
SimpleTemplate (T obj) : m_obj(std::move(obj)) { }
void operator() () { m_obj.DoSomething(); }
};
template <class T>
class SimpleTemplate<T, true> {
private:
T m_collection;
public:
SimpleTemplate (T obj) : m_collection(std::move(obj)) { }
void operator() () {
for (auto && obj : m_collection) { obj.DoSomething(); }
}
};
由于两个偏特化对于任何给定的 T
都是互斥的,因此您不会收到任何关于歧义的错误。
编辑: 将第二个模板参数更改为 bool 而不是 class。这使得完全专门化它变得简单,以防不需要默认行为。
例如对于 std::string
,其中 is_iterable
为真,只需执行以下操作。请注意,我向 SimpleTemplate 添加了构造函数,否则我无法获得完整的专业化来继承基础 class' 构造函数。
template <>
class SimpleTemplate<std::string, true>
: public SimpleTemplate<std::string, false> {
// Inherit constructor.
using base = SimpleTemplate<std::string, false>;
using base::base;
};
假设我有一个模板设置来对类型执行某些操作,例如...
template<typename T>
class SimpleTemplate
{
private:
T m_obj;
public:
void operator()() { m_obj.DoSomething(); }
};
而且我想以同样的方式处理我有一个类型为 T 的集合的情况。我目前有一个类似矢量的模板设置...
template<typename T>
class SimpleTemplate<std::vector<T>>
{
private:
std::vector<T> m_collection;
public:
void operator()()
{
for (auto&& obj : m_collection) obj.DoSomething();
}
};
现在我也想支持集合,unordered_sets等等。我可以为每个集合编写一个模板,但我觉得这对于模板来说应该是一个完美的工作,只是我不知道应该如何编写,或者即使它可以?
我可以做类似 template<typename C<T>>
的事情吗?
Now I want to also support sets, unordered_sets and so on. I could write a template for each collection but I feel like this should be a perfect job for a template, only I can't figure out how it should be written, or even if it can be
也许你可以使用模板模板参数
template <template <typename...> class C, typename... Ts>
class SimpleTemplate<C<Ts...>>
{
private:
C<Ts...> m_collection;
public:
void operator()()
{
for (auto&& obj : m_collection) obj.DoSomething();
}
};
这应该截取std::(unordered_)(multi)set
、std::vector
、std::deque
等
不幸的是没有拦截std::array
,因为它的第二个模板参数是一个值,而不是类型。
正如 Geoffroy 所提到的,您可以使用特征来检测 T
是否可以迭代。然后你用它来 select 正确的专业化。
因此,从 Jarod42 here:
所展示的 "is_iterable" 特征开始// Code by Jarod42 (
#include <iterator>
#include <type_traits>
namespace detail
{
// To allow ADL with custom begin/end
using std::begin;
using std::end;
template <typename T>
auto is_iterable_impl(int)
-> decltype (
begin(std::declval<T&>()) != end(std::declval<T&>()), // begin/end and operator !=
void(), // Handle evil operator ,
++std::declval<decltype(begin(std::declval<T&>()))&>(), // operator ++
void(*begin(std::declval<T&>())), // operator*
std::true_type{});
template <typename T>
std::false_type is_iterable_impl(...);
}
template <typename T>
using is_iterable = decltype(detail::is_iterable_impl<T>(0));
这为您提供了一个 is_iterable<T>
特征,它继承自 std::true_type
或 std::false_type
。现在将其与 SFINAE 一起使用来创建两个专业:
template <class T, bool = is_iterable<T>::value>
class SimpleTemplate;
template <class T>
class SimpleTemplate<T, false> {
private:
T m_obj;
public:
SimpleTemplate (T obj) : m_obj(std::move(obj)) { }
void operator() () { m_obj.DoSomething(); }
};
template <class T>
class SimpleTemplate<T, true> {
private:
T m_collection;
public:
SimpleTemplate (T obj) : m_collection(std::move(obj)) { }
void operator() () {
for (auto && obj : m_collection) { obj.DoSomething(); }
}
};
由于两个偏特化对于任何给定的 T
都是互斥的,因此您不会收到任何关于歧义的错误。
编辑: 将第二个模板参数更改为 bool 而不是 class。这使得完全专门化它变得简单,以防不需要默认行为。
例如对于 std::string
,其中 is_iterable
为真,只需执行以下操作。请注意,我向 SimpleTemplate 添加了构造函数,否则我无法获得完整的专业化来继承基础 class' 构造函数。
template <>
class SimpleTemplate<std::string, true>
: public SimpleTemplate<std::string, false> {
// Inherit constructor.
using base = SimpleTemplate<std::string, false>;
using base::base;
};