专门为 T 类型的容器设计模板

问题描述 投票:0回答:3

鉴于我有一个模板设置可以对诸如...之类的类型执行某些操作

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>>
这样的事情吗?

c++ templates containers specialization partial-specialization
3个回答
4
投票

正如 Geoffroy 提到的,您可以使用特征来检测

T
是否可以迭代。然后,您可以使用它来选择正确的专业。

所以从 Jarod42 here:

显示的“is_iterable”特征开始
// Code by Jarod42 (https://stackoverflow.com/a/29634934).
#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
都是互斥的,因此您不会收到任何有关歧义的错误。

编辑:将第二个模板参数更改为布尔值而不是类。这使得完全专业化它变得简单,以防不需要默认行为。

例如对于

std::string
(其中
is_iterable
为真),只需执行以下操作即可。请注意,我向 SimpleTemplate 添加了构造函数,否则我无法获得完整的专业化来继承基类的构造函数。

template <>
class SimpleTemplate<std::string, true>
    : public SimpleTemplate<std::string, false> {
  // Inherit constructor.
  using base = SimpleTemplate<std::string, false>;
  using base::base;
};

1
投票

现在我还想支持集合、unordered_sets 等等。我可以为每个集合编写一个模板,但我觉得这对于模板来说应该是一个完美的工作,只是我不知道它应该如何编写,或者即使它可以

也许你可以使用模板-模板参数

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
,因为它的第二个模板参数是一个值,而不是一个类型。


0
投票

从 C++20 开始,可以使用

std::forward_range
的概念来约束模板类。它本质上是范围的退休,
std::ranges::begin()
函数返回
std::forward_iterator
的模型。

示例:

template <std::ranges::forward_range T>
class SimpleTemplate
{
private:
  T  m_collection;

public:
  void operator()()
  {
    for (auto&& obj : m_collection)
      obj.DoSomething();
  }
};
© www.soinside.com 2019 - 2024. All rights reserved.