STL容器迭代器的模板特化

Template specialization for iterators of STL containers?

本文关键字:迭代器 STL      更新时间:2023-10-16

我正在尝试编写一个类似于std::to_string的模板函数,适用于基本类型以及STL容器的迭代器。但是我不确定如何编写足够具体的模板来识别迭代器。

到目前为止我所尝试的是尝试在STL容器中使用iterator类型定义

  template<typename... Args, template <typename...> class Container>
  static string to_string(typename Container<Args...>::iterator s) { ...

下面是一个最小的例子。代码编译后,模板函数My::to_string无法匹配上述签名,将std::set<int>::iterator作为默认类型处理。

我的问题是如何以一种通用的方式正确地编写它,以便模板函数My::to_string可以拾取迭代器,但不会将迭代器与其他标准模板类型(如std::string)混淆。

提前感谢。

#include <set>
#include <iostream>
using namespace std;
class My{
  //base case
  template<typename T>
  static string to_string(const T& t) {
    return "basic ";
  }
  //specialization for string
  template <typename Char, typename Traits, typename Alloc>
  static string to_string(const std::basic_string<Char, Traits, Alloc>& s) {
    return (string)s;
  }
  //Problem line: how to write specialization for iterators of standard containers?
  template<typename... Args, template <typename...> class Container>
  static string to_string(typename Container<Args...>::iterator s) {
    return "itor ";
  }
};
int main() {
  int i =  2;
  string str = "Hello";
  set<int> s;
  s.insert(i);
  cout << to_string(i) << ", " << str << ", "
       << to_string(s.begin()) << endl;   //didn't get captured by iterator spec.
}
输出:

basic, Hello, basic
所需输出:

basic, Hello, itor

如果您只关心参数的迭代器性,而不关心容器的类型,那么您可以SFINAE出另一个重载。

首先生成一个is_iterator性状,如下图所示:

template <typename T>
struct sfinae_true : std::true_type {};
struct is_iterator_tester {
    template <typename T>
    static sfinae_true<typename std::iterator_traits<T>::iterator_category> test(int);
    template <typename>
    static std::false_type test(...);
};
template <typename T>
struct is_iterator : decltype(is_iterator_tester::test<T>(0)) {};

现在SFINAE出错误的重载取决于类型是否是迭代器:

//base case
template<typename T>
static std::enable_if_t<!is_iterator<T>::value, string> to_string(const T& t) {
  return "basic ";
}
//specialization for string
template <typename Char, typename Traits, typename Alloc>
static string to_string(const std::basic_string<Char, Traits, Alloc>& s) {
  return (string)s;
}
//Problem line: how to write specialization for iterators of standard containers?
template<typename T>
static std::enable_if_t<is_iterator<T>::value, string> to_string(const T& s) {
  return "itor ";
}

演示。

如果您将特定于迭代器的重载约束为与定义了operator*的任何类型一起工作(Live at Coliru),则这非常简单:

namespace My {
    // base case
    using std::to_string;
    // overload for strings
    template <typename Char, typename Traits, typename Alloc>
    std::basic_string<Char, Traits, Alloc>
    to_string(std::basic_string<Char, Traits, Alloc> s) {
        return s;
    }
    // overload for iterators
    template<typename Iterator>
    auto to_string(Iterator i) -> decltype(to_string(*i)) {
        return "iterator to: "" + to_string(*i) + '"';
    }
}

应该是这样的:

template<typename ForwardIterator>
static string to_string(ForwardIterator begin, ForwardIterator end) {
    return "itor ";
}