如何打印通用 std::list 迭代器?

mad*_*ina 6 c++ templates iterator c++-standard-library c++17

我希望能够std::list通过打印其值来打印任何迭代器。我的初始代码如下所示:

template<typename T>
std::ostream& operator<<(std::ostream& os, const typename std::list<T>::const_iterator& x)
{
   return os << "&" << *x;
}

Run Code Online (Sandbox Code Playgroud)

哪个不起作用,因为编译器无法确定参数T。然后我尝试使它在迭代器类型本身上通用,并iterator_traits用于将其限制为迭代器。

template<
    typename It,
    typename = typename std::iterator_traits<It>::value_type
>
std::ostream &operator<<(std::ostream &os, const It &x)
{
    return os << "&" << *x;
}
Run Code Online (Sandbox Code Playgroud)

但是,当然,我得到了两个相互冲突的实现std::ostream << *const char,因为指针也是迭代器。如何将实现限制为std::list迭代器,以免发生冲突?

son*_*yao 6

您可以约束类型iteratorconst_iteratorstd::list。例如

template<typename It>
std::enable_if_t<std::is_same_v<It, typename std::list<typename std::iterator_traits<It>::value_type>::iterator> ||
                 std::is_same_v<It, typename std::list<typename std::iterator_traits<It>::value_type>::const_iterator>
                 , std::ostream &> 
operator<<(std::ostream &os, const It &x) {
    return os << "&" << *x;
}
Run Code Online (Sandbox Code Playgroud)


JeJ*_*eJo 5

您可以SFINAEconst char*从出operator<<过载。

#include <type_traits> // std::enable_if_t, std::is_same_v, std::remove_reference_t

template<
    typename It,
    typename = typename std::iterator_traits<It>::value_type
>
auto operator<<(std::ostream &os, const It &x)
-> std::enable_if_t< !std::is_same_v<std::remove_reference_t<It>, const char*>, std::ostream&>
{
    return os << "&" << *x;
}
Run Code Online (Sandbox Code Playgroud)

见演示

请注意,以上不仅限于 forstd::list::iterator,这意味着来自其他容器的迭代器也可以考虑此重载。这可能不是您想要的行为。


由于我们无法从 iterator 获取容器类型,我建议与评论中提到的@super相同。为Legacy Bidirectional Iterator提供一个operator<<重载, 这就是它所拥有的。std::list

以下是一个示例代码,它适用于您预期的情况以及满足双向迭代器要求的所有容器。

#include <list>
#include <iostream>
#include <iterator>    // std::iterator_traits, std::bidirectional_iterator_tag
#include <type_traits> // std::is_same_v, std::enable_if_t

// SFINAE helper  type for bidirectional_iterator_t
template<typename Iterator, typename ReType = void>
using enable_for_bidirectional_iterator_t
= std::enable_if_t<
   std::is_same_v<std::bidirectional_iterator_tag, typename std::iterator_traits<Iterator>::iterator_category>
   , ReType
>;

template<typename Iterator>
auto operator<<(std::ostream& os, const Iterator x) noexcept
-> enable_for_bidirectional_iterator_t<Iterator, std::ostream&>
{
   return os << "&" << *x;
}
Run Code Online (Sandbox Code Playgroud)

见演示


但是,通常,您为operator<<容器而不是迭代器提供重载。您可能需要重新考虑设计。