基于范围的 for 循环与自动说明符结合 static_cast
Range-based for loop with auto specifier combined with static_cast
想象我有一个 std::string
的 std::vector
,我想在基于范围的 for 循环中将那些 std::string
转换为 std::string_view
:
auto v = std::vector<std::string>{"abc", "def", "ghi"};
for (std::string_view sv : v) {
// do something with string_view
}
上面的代码完全有效,但我想保留 auto
说明符来做到这一点,如何在一行基于范围的 for 循环中做到 static_cast
像这样?看起来 C++20 ranges
可以用简洁的方式做到这一点,有人可以举个例子吗?
for (auto sv : v | static_cast<std::string_view>) {
// do something with std::string_view
}
正如@Elijay 评论的那样,您可以简单地创建一个新的 vector
of string_view
s:
for (auto sv : vector<string_view>(v.begin(), v.end()))
但这种做法首先违背了使用 string_view
的全部目的:避免复制。
正如下面评论的那样,它也有点违背 auto
的全部目的:避免不必要地重述类型信息。这里是第一次引入类型,所以必须明确说明。为什么不直接放在前面一目了然?
减少冗长并按预期使用 string_view
是您开始的地方:
for (string_view sv : v)
你可以这样做:
#include <string>
#include <vector>
#include <iostream>
#include <string_view>
int main() {
auto v = std::vector<std::string>{"abc", "def", "ghi"};
for (auto sv : std::vector<std::string_view>(v.begin(), v.end())) {
// use sv ...
}
}
但是请注意,完全不推荐创建一个全新的向量。它再次分配内存并导致很多不必要的开销。此外,无论如何您都必须在某处拼写类型,因此 auto
在这里没有任何优势。正确的做法TM 是明确指定类型名称而不是使用 auto
.
并不是说这是一个好主意,但这可能是一个更通用的 transform 概念(和一个邪恶的 lambda 技巧)的有用示例:
for(auto sv : v |
views::transform([](std::string_view x) {return x;})) …
迭代器是一个很好的定制点,不幸的是它需要相当多的样板文件:
#include <vector>
#include <iostream>
#include <string_view>
template <typename T>
struct container_view {
const T& container;
container_view(const T& t) : container(t) {}
struct iterator{
typename T::const_iterator base;
iterator(const typename T::const_iterator& it) : base(it) {}
bool operator!=(const iterator& other) { return base != other.base; }
iterator& operator++() { ++base; return *this; }
std::string_view operator*() { return {*base}; }
// ^--- string_view
};
iterator begin() { return {container.begin()};}
iterator end() { return {container.end()};}
};
int main (){
auto v = std::vector<std::string>{"abc", "def", "ghi"};
// v--- auto
for (auto sv : container_view(v)) {
std::cout << sv << '\n';
}
}
可以说 more/less 简洁。意见会有所不同。
#include <vector>
#include <string_view>
#include <string>
#include <iostream>
#include <range/v3/view/transform.hpp>
int main()
{
auto v = std::vector<std::string>{"abc", "def", "ghi"};
using namespace ranges;
auto as_string_view = views::transform([](auto&& x) { return std::string_view(x); });
for (auto sv : v | as_string_view) {
std::cout << sv << '\n';
}
}
想象我有一个 std::string
的 std::vector
,我想在基于范围的 for 循环中将那些 std::string
转换为 std::string_view
:
auto v = std::vector<std::string>{"abc", "def", "ghi"};
for (std::string_view sv : v) {
// do something with string_view
}
上面的代码完全有效,但我想保留 auto
说明符来做到这一点,如何在一行基于范围的 for 循环中做到 static_cast
像这样?看起来 C++20 ranges
可以用简洁的方式做到这一点,有人可以举个例子吗?
for (auto sv : v | static_cast<std::string_view>) {
// do something with std::string_view
}
正如@Elijay 评论的那样,您可以简单地创建一个新的 vector
of string_view
s:
for (auto sv : vector<string_view>(v.begin(), v.end()))
但这种做法首先违背了使用 string_view
的全部目的:避免复制。
正如下面评论的那样,它也有点违背 auto
的全部目的:避免不必要地重述类型信息。这里是第一次引入类型,所以必须明确说明。为什么不直接放在前面一目了然?
减少冗长并按预期使用 string_view
是您开始的地方:
for (string_view sv : v)
你可以这样做:
#include <string>
#include <vector>
#include <iostream>
#include <string_view>
int main() {
auto v = std::vector<std::string>{"abc", "def", "ghi"};
for (auto sv : std::vector<std::string_view>(v.begin(), v.end())) {
// use sv ...
}
}
但是请注意,完全不推荐创建一个全新的向量。它再次分配内存并导致很多不必要的开销。此外,无论如何您都必须在某处拼写类型,因此 auto
在这里没有任何优势。正确的做法TM 是明确指定类型名称而不是使用 auto
.
并不是说这是一个好主意,但这可能是一个更通用的 transform 概念(和一个邪恶的 lambda 技巧)的有用示例:
for(auto sv : v |
views::transform([](std::string_view x) {return x;})) …
迭代器是一个很好的定制点,不幸的是它需要相当多的样板文件:
#include <vector>
#include <iostream>
#include <string_view>
template <typename T>
struct container_view {
const T& container;
container_view(const T& t) : container(t) {}
struct iterator{
typename T::const_iterator base;
iterator(const typename T::const_iterator& it) : base(it) {}
bool operator!=(const iterator& other) { return base != other.base; }
iterator& operator++() { ++base; return *this; }
std::string_view operator*() { return {*base}; }
// ^--- string_view
};
iterator begin() { return {container.begin()};}
iterator end() { return {container.end()};}
};
int main (){
auto v = std::vector<std::string>{"abc", "def", "ghi"};
// v--- auto
for (auto sv : container_view(v)) {
std::cout << sv << '\n';
}
}
可以说 more/less 简洁。意见会有所不同。
#include <vector>
#include <string_view>
#include <string>
#include <iostream>
#include <range/v3/view/transform.hpp>
int main()
{
auto v = std::vector<std::string>{"abc", "def", "ghi"};
using namespace ranges;
auto as_string_view = views::transform([](auto&& x) { return std::string_view(x); });
for (auto sv : v | as_string_view) {
std::cout << sv << '\n';
}
}