基于范围的 for 循环与自动说明符结合 static_cast

Range-based for loop with auto specifier combined with static_cast

想象我有一个 std::stringstd::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_views:

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