在 C++20 中,什么时候应该使用 `iterator_traits<I>::value_type` 什么时候应该使用 `iter_value_t`?
In C++20 when should use `iterator_traits<I>::value_type` and when should I use `iter_value_t`?
C++20 概念添加了一个 alternative way 来访问迭代器特征。例如 iter_value_t<I>
给出了与 iterator_traits<I>::value_type
相似的结果。 我注意到 iterator_traits
似乎在概念约束中不起作用。
iter_value_t
适用于概念,但它也适用于其他任何地方。
所以我的直觉是我应该使用更新的 iter_value_t
因为它在两种情况下都有效。
这是正确的吗?我应该在什么时候选择其中之一?
编辑: 在概念约束中使用 iterator_traits 时我丢失了 typename
。这些概念让我感到不安!
iter_value_t<I>
用于根据间接可读类型实现算法。 iterator_traits<I>
用于根据迭代器实现算法。
间接可读类型是可以通过应用 operator*
读取的类型。这包括指针、智能指针和迭代器。所有这些类型都满足 indirectly_readable
概念。
要完全理解 iter_value_t<I>
背后的想法,我们需要看一下它的实现。
如果std::iterator_traits<std::remove_cvref_t<T>>
没有特化,那么std::iter_value_t<T>
就是std::indirectly_readable_traits<std::remove_cvref_t<T>>::value_type
。否则就是std::iterator_traits<std::remove_cvref_t<T>>::value_type
.
您可以看到,如果可能的话,它会尝试默认为 iterator_traits,但还会对类型应用 remove_cvref_t
转换。这允许它使用 const-volatile-reference 限定类型,例如 const char* const
或 const char*&
等
Is this correct?
不,iterator_traits<I>
也处理概念(除非我误解了你的意思)。
#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.
When should I prefer one or the other?
除非(不太可能)您对某些 iterator_traits
功能有特定需求,否则请将 iter_value_t<T>
与其家族的其他成员一起使用,例如 iter_reference_t
or iter_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<I>::value_type
在这种情况下会失败。
运行 代码 here.
间接可读特征和迭代器特征的概念在标准中并排解释,可以在 (c++23 n4892 working draft):
中找到
您应该始终使用 std::iter_value_t<I>
。
首先简单考虑一下长度:
std::iter_value_t<I>
对比
typename std::iterator_traits<I>::value_type
因为 value_type
是一个依赖类型,你需要那个额外的 typename
关键字,所以这是 一口.
其次,前者简单定义的情况比后者多。您通常会按值获取迭代器,因此 运行 可能不是一个大问题,但 std::iter_value_t<int*&>
仍然是 int
而 std::iterator_traits<int*&>::value_type
不是定义。 std::iterator_traits<int* const>::value_type
也一样。也就是说,std::iterator_traits<I>::value_type
有效,但 std::iterator_traits<I const>::value_type
或 std::iterator_traits<I&>::value_type
无效——您必须编写更多内容以确保您传递的是非引用、非 cv-合格类型。
还有其他一些小的边缘情况,例如 std::iter_value_t<std::shared_ptr<int>>
是 int
但 std::iterator_traits<std::shared_ptr<int>>::value_type
未定义。因此,如果您正在编写一种算法,该算法接受一个不一定是迭代器的任意间接可读对象,您仍然可以使用它。
所以只使用总是有效的短的东西,而不是有时不起作用的更长的东西。
通常,std::iter_reference_t<I>
也比 std::iter_value_t<I>
有用得多。
C++20 概念添加了一个 alternative way 来访问迭代器特征。例如 iter_value_t<I>
给出了与 iterator_traits<I>::value_type
相似的结果。 我注意到
iterator_traits
似乎在概念约束中不起作用。iter_value_t
适用于概念,但它也适用于其他任何地方。
所以我的直觉是我应该使用更新的 iter_value_t
因为它在两种情况下都有效。
这是正确的吗?我应该在什么时候选择其中之一?
编辑: 在概念约束中使用 iterator_traits 时我丢失了 typename
。这些概念让我感到不安!
iter_value_t<I>
用于根据间接可读类型实现算法。 iterator_traits<I>
用于根据迭代器实现算法。
间接可读类型是可以通过应用 operator*
读取的类型。这包括指针、智能指针和迭代器。所有这些类型都满足 indirectly_readable
概念。
要完全理解 iter_value_t<I>
背后的想法,我们需要看一下它的实现。
如果std::iterator_traits<std::remove_cvref_t<T>>
没有特化,那么std::iter_value_t<T>
就是std::indirectly_readable_traits<std::remove_cvref_t<T>>::value_type
。否则就是std::iterator_traits<std::remove_cvref_t<T>>::value_type
.
您可以看到,如果可能的话,它会尝试默认为 iterator_traits,但还会对类型应用 remove_cvref_t
转换。这允许它使用 const-volatile-reference 限定类型,例如 const char* const
或 const char*&
等
Is this correct?
不,iterator_traits<I>
也处理概念(除非我误解了你的意思)。
#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.
When should I prefer one or the other?
除非(不太可能)您对某些 iterator_traits
功能有特定需求,否则请将 iter_value_t<T>
与其家族的其他成员一起使用,例如 iter_reference_t
or iter_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<I>::value_type
在这种情况下会失败。
运行 代码 here.
间接可读特征和迭代器特征的概念在标准中并排解释,可以在 (c++23 n4892 working draft):
中找到您应该始终使用 std::iter_value_t<I>
。
首先简单考虑一下长度:
std::iter_value_t<I>
对比
typename std::iterator_traits<I>::value_type
因为 value_type
是一个依赖类型,你需要那个额外的 typename
关键字,所以这是 一口.
其次,前者简单定义的情况比后者多。您通常会按值获取迭代器,因此 运行 可能不是一个大问题,但 std::iter_value_t<int*&>
仍然是 int
而 std::iterator_traits<int*&>::value_type
不是定义。 std::iterator_traits<int* const>::value_type
也一样。也就是说,std::iterator_traits<I>::value_type
有效,但 std::iterator_traits<I const>::value_type
或 std::iterator_traits<I&>::value_type
无效——您必须编写更多内容以确保您传递的是非引用、非 cv-合格类型。
还有其他一些小的边缘情况,例如 std::iter_value_t<std::shared_ptr<int>>
是 int
但 std::iterator_traits<std::shared_ptr<int>>::value_type
未定义。因此,如果您正在编写一种算法,该算法接受一个不一定是迭代器的任意间接可读对象,您仍然可以使用它。
所以只使用总是有效的短的东西,而不是有时不起作用的更长的东西。
通常,std::iter_reference_t<I>
也比 std::iter_value_t<I>
有用得多。