【问题标题】:Template specialization for iterators of STL containers?STL容器迭代器的模板特化?
【发布时间】:2014-09-10 23:00:03
【问题描述】:

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

到目前为止,我尝试的是尝试在 STL 容器中使用 iterator typedef

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

下面附上了一个最小的例子。代码编译成功,但模板函数My::to_string 无法匹配上述签名,并将std::set&lt;int&gt;::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

【问题讨论】:

  • :: 的左边是非推断上下文。这也可能通常是不可能的。如果两个容器都使用原始指针作为迭代器怎么办?

标签: c++ templates c++11 stl iterator


【解决方案1】:

如果你只关心参数的iterator-ness,而不关心容器的类型,那么你可以SFINAE out 其他重载。

首先制作一个is_iterator trait,如this answer所示:

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 ";
}

Demo.

【讨论】:

  • 谢谢。我看到你的演示使用了-std=c++1y。我用 gcc-4.8.1 试过这个,但它没有编译,说error: 'enable_if_t' in namespace 'std' does not name a type。这需要gcc4.9吗?
  • @TingL 如果您的编译器不支持,请将std::enable_if_t&lt;...&gt; 替换为typename std::enable_if&lt;...&gt;::type
  • 谢谢,您的解决方案适用于template &lt;bool B, typename T = void&gt; using enable_if_t = typename std::enable_if&lt;B, T&gt;::type;
【解决方案2】:

如果您将特定于迭代器的重载限制为使用定义了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) + '"';
    }
}

【讨论】:

  • 谢谢。这简化了事情。
  • 请注意,如果传递的类型不能传递给 std::to_string 并且不是迭代器或字符串,则不会编译。此外,to_string('a') 会给你 97,因为std::to_string 没有比int 更小的重载。根据用例,这可能重要也可能不重要。
【解决方案3】:

应该更像这样:

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

【讨论】:

  • to_string 带两个参数?这有点违反直觉
  • @MooingDuck:因为你对单个迭代器所能做的就是取消引用它,一个 to_string 采用单个迭代器对我来说似乎毫无意义。 to_string(it) 与已经有效的 to_string(*it) 相比有什么好处?也许我误解了 OP 的问题,但听起来重点是要对容器进行字符串化。
  • @MooingDuck 另一方面,大多数只使用一个迭代器的函数要么没有意义,要么是错误的。
  • @JohnZwinck:我在 OP 中没有看到任何关于字符串化容器的内容,但另一方面,我想不出任何其他可能的用途,所以我猜两个迭代器确实有意义。跨度>
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-25
相关资源
最近更新 更多