【问题标题】:How to print generic std::list iterator?如何打印通用 std::list 迭代器?
【发布时间】:2020-12-02 00:25:48
【问题描述】:

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

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

这不起作用,因为编译器无法确定参数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;
}

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

【问题讨论】:

  • 如果您将迭代器传递给函数,您将永远无法知道它来自哪个容器。这就是迭代器的全部目的,你不需要知道容器是什么,只需要知道你有什么样的迭代器。您可以将您的功能限制为 LegacyBidirectionalIterator,这是 std::list 使用的,但为什么呢?

标签: c++ templates iterator c++17 c++-standard-library


【解决方案1】:

您可以将类型限制为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;
}

【讨论】:

    【解决方案2】:

    您可以将SFINAE const char*operator&lt;&lt; 过载中取出。

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

    (See a Demo)

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


    既然我们could not get the container type from the iterator,我建议和cmets中提到的@super一样。 为 Legacy Bidirectional Iterator 提供 operator&lt;&lt; 重载 这就是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;
    }
    

    (See a Demo)


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

    【讨论】:

    • 感谢您的回答。我需要为迭代器重载它,因为我将它们用于自引用数据结构。
    • @madlaina 是的,那么你选择了宋元瑶的正确答案。这是最好的方法。
    猜你喜欢
    • 2018-09-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-05
    • 1970-01-01
    • 1970-01-01
    • 2012-05-07
    相关资源
    最近更新 更多