【问题标题】:In C++20 when should use `iterator_traits<I>::value_type` and when should I use `iter_value_t`?在 C++20 中,什么时候应该使用 `iterator_traits<I>::value_type`,什么时候应该使用 `iter_value_t`?
【发布时间】:2021-08-21 19:36:43
【问题描述】:

C++20 概念添加了一个alternative way 来访问迭代器特征。例如iter_value_t&lt;I&gt; 给出与iterator_traits&lt;I&gt;::value_type 相似的结果。我注意到iterator_traits 在概念约束中似乎不起作用。 iter_value_t 适用于概念,但也适用于其他任何地方。 所以我的预感是我应该使用较新的iter_value_t,因为它适用于两种情况。

这是正确的吗?我什么时候应该选择其中一个?

编辑:我在概念约束中使用 iterator_traits 时缺少 typename。这些概念让我感到不安!

【问题讨论】:

  • 什么意思,它似乎在概念约束下不起作用?
  • @Barry 我收到了一个编译器错误,但看看另一个答案,我可能只是错过了“typename”。既然你问了,我就给你做个小例子。

标签: c++ c++20 c++-concepts


【解决方案1】:

您应该始终使用std::iter_value_t&lt;I&gt;

首先,简单考虑长度:

std::iter_value_t<I>

对比

typename std::iterator_traits<I>::value_type

因为value_type 是一个依赖类型,你需要额外的typename 关键字,所以这是一口

其次,前者在比后者更多的情况下被简单地定义。您通常会按值获取迭代器,因此这可能不是您会遇到的大问题,但std::iter_value_t&lt;int*&amp;&gt; 仍然是int,而std::iterator_traits&lt;int*&amp;&gt;::value_type 未定义。 std::iterator_traits&lt;int* const&gt;::value_type 也一样。也就是说,std::iterator_traits&lt;I&gt;::value_type 有效,但 std::iterator_traits&lt;I const&gt;::value_typestd::iterator_traits&lt;I&amp;&gt;::value_type 无效 - 您必须编写更多内容以确保您传递的是非引用、非 cv 限定类型。

还有一些小的其他边缘情况,例如 std::iter_value_t&lt;std::shared_ptr&lt;int&gt;&gt;int 但未定义 std::iterator_traits&lt;std::shared_ptr&lt;int&gt;&gt;::value_type。因此,如果您编写的算法采用任意间接可读的不一定是迭代器,您仍然可以使用它。

所以只使用总是有效的短的东西,而不是有时不起作用的更长的东西。


通常,std::iter_reference_t&lt;I&gt; 也比 std::iter_value_t&lt;I&gt; 有用得多。

【讨论】:

    【解决方案2】:

    iter_value_t&lt;I&gt; 用于实现间接可读类型的算法。 iterator_traits&lt;I&gt; 用于在迭代器方面实现算法。

    间接可读类型是可以通过应用operator* 读取的类型。这包括指针、智能指针和迭代器。所有这些类型都满足indirectly_readable 概念。

    要完全理解 iter_value_t&lt;I&gt; 背后的想法,我们需要看看它的实现。

    如果std::iterator_traits&lt;std::remove_cvref_t&lt;T&gt;&gt; 不是特化的,那么std::iter_value_t&lt;T&gt; 就是std::indirectly_readable_traits&lt;std::remove_cvref_t&lt;T&gt;&gt;::value_type。否则为std::iterator_traits&lt;std::remove_cvref_t&lt;T&gt;&gt;::value_type

    您可以看到,如果可能的话,它会尝试默认为 iterator_traits,但还会将remove_cvref_t 转换应用于类型。这允许它使用 const-volatile-reference 限定类型,例如 const char* constconst char*&amp; 等。

    这对吗?

    不,iterator_traits&lt;I&gt; 也可以处理概念(除非我误解了您的意思)。

    #include <vector>
    #include <iostream>
    #include <concepts>
    #include <type_traits>
    #include <iterator>
    
    template<class T>
    concept my_iterator_concept = 
        std::is_same_v<typename std::iterator_traits<T>::value_type, int>;
    
    int main()
    {
        std::vector<int> v;
        std::cout << std::boolalpha << my_iterator_concept<typename decltype(v)::iterator>;
    }
    

    运行代码here

    我什么时候应该选择其中一个?

    除非(不太可能)您对某些iterator_traits 功能有特殊需求,否则请与iter_value_t&lt;T&gt; 及其家族的其他成员一起使用,例如iter_reference_titer_difference_t

    这是一个为什么你应该喜欢它的例子。这是deduction guide for basic_string view

    template<class It, class End>
    basic_string_view(It, End) -> basic_string_view<iter_value_t<It>>;
    

    您可能只想传递 const char* const 作为迭代器的替代方法,如下所示:

    // Requires c++ 20
    #include <string_view>
    #include <iostream>
    
    int main()
    {
        const char* const c = "example";
        auto str = std::string_view(c, c + 3);
        std::cout << str;
    }
    

    iterator_traits&lt;I&gt;::value_type 在这种情况下会失败。 运行代码here

    间接可读特征和迭代器特征的概念在标准中相互解释,可以在 (c++23 n4892 working draft) 中找到:

    23.3.2.2 Indirectly readable traits

    23.3.2.3 Iterator traits

    【讨论】:

      猜你喜欢
      • 2010-10-18
      • 2010-12-30
      • 2023-04-02
      • 2011-04-15
      • 2017-04-10
      • 2012-03-19
      • 2018-05-12
      • 2018-12-11
      相关资源
      最近更新 更多