Mat*_*ias 4 c++ functional-programming c++20 std-ranges
当我使用 Stream Library ( http://jscheiny.github.io/Streams/api.html# ) 时,我可以像在 Java-Streams 中那样做类似的事情:
#include "Streams/source/Stream.h"
#include <iostream>
using namespace std;
using namespace stream;
using namespace stream::op;
int main() {
    list<string> einkaufsliste = {
        "Bier", "Käse", "Wurst", "Salami", "Senf", "Sauerkraut"
    };
    int c = MakeStream::from(einkaufsliste)
          | filter([] (string s) { return !s.substr(0,1).compare("S"); })
          | peek([] (string s) { cout << s << endl; })
          | count()
          ;
    cout << c << endl;
}
它给出了这个输出:
Salami
Senf
Sauerkraut
3
在 C++20 中,我发现了范围,它看起来很有希望实现同样的目标。但是,当我想构建类似的函数式编程风格时,它不起作用:
Salami
Senf
Sauerkraut
3
尽管像这样的文章(https://www.modernescpp.com/index.php/c-20-the-ranges-library)暗示了这样的功能,但接缝并不意味着像这样工作。
test.cpp:16:67: note:   candidate expects 3 arguments, 1 provided
   16 |             | ranges::for_each([] (string s) { cout << s << " "; })
      |                                                                   ^
test.cpp:17:29: error: no match for call to '(const std::ranges::__count_fn) ()'
   17 |             | ranges::count();
      |                             ^
任何想法如何我仍然可以在 C++20 中做类似的事情?
这里的每个适配器都有问题。
首先,filter:
| ranges::views::filter([] (string s) { return !s.substr(0,1).compare("S"); })
这会复制每个字符串,然后从每个字符串中创建一个新字符串,只是为了检查第一个字符是否为S. 你绝对应该把绳子带到const&这里。然后,由于问题被标记为 C++20:
| ranges::views::filter([](string const& s) { return !s.starts_with('S'); })
二、for_each:
| ranges::for_each([] (string s) { cout << s << " "; })
ranges::for_each 不是范围适配器——它是一种算法,它在每个元素上调用可调用对象,但它不返回新范围,因此它不能适应这样的管道。
Ranges 没有这样的peek适配器(既不是 C++20 也不是 range-v3)但是我们可以尝试根据transform使用标识来实现它:
auto peek = [](auto f){
    return ranges::views::transform([=]<typename T>(T&& e) -> T&& {
        f(e);
        return std::forward<T>(e);
    });
};
现在你可以写(同样,字符串应该被当作const&这里):
| peek([](std::string const& s){ cout << s << " "; })
但这实际上只有在我们访问范围内的任何元素时才有效,并且您的代码中没有任何内容必须这样做(我们不需要读取任何元素来找到距离,我们只需要推进迭代器根据需要多次)。所以你会发现上面的内容实际上并没有打印任何东西。
所以相反,for_each是正确的方法,我们只需要分开做:
auto f = einkaufsliste
       | ranges::views::filter([](string const& s) { return s.starts_with('S'); });
ranges::for_each(f, [](string const& s){ cout << s << " "; });
那肯定会打印每个元素。
最后,count:
| ranges::count();
在 Ranges 中,只有返回视图的适配器是可管道化的。count()只是不能这样工作。此外,count()需要第二个参数,即您正在计算的东西。count(r, value)计算valuein的实例r。没有一元count(r)。
您正在寻找的算法已命名distance(同样,不能通过管道传输)。
所以你必须像这样写整个事情:
int c = ranges::distance(f);
这是P2011 的部分动机,能够实际count以线性顺序在末尾而不是在前面写入(无需进行更多的库更改)。