C++11/14是否支持Groovy风格的集合链式Lambda调用?
Great question! Let's break down how to replicate Groovy's fluent collection operations in C++11/14, including both existing solutions and custom implementation options.
First: The C++ Standard Library Reality
C++11/14's standard library doesn't offer Groovy-style chained member functions for collections out of the box. Instead, it uses standalone algorithms (like std::for_each, std::transform, std::copy_if) that operate on iterators, which means you'll need intermediate containers for multi-step operations.
For example, your Groovy code:
["a", "b"].each { println(it) } [1, 2].collect { it * 2 }.grep { it % 3 }.each { println(it) }
Translates to standard C++ like this:
#include <vector> #include <algorithm> #include <iostream> #include <iterator> // Equivalent of ["a", "b"].each std::vector<std::string> strs = {"a", "b"}; std::for_each(strs.begin(), strs.end(), [](const auto& s) { std::cout << s << '\n'; }); // Equivalent of [1,2].collect(...).grep(...).each std::vector<int> nums = {1, 2}; std::vector<int> doubled; std::transform(nums.begin(), nums.end(), std::back_inserter(doubled), [](int i) { return i * 2; }); std::vector<int> filtered; std::copy_if(doubled.begin(), doubled.end(), std::back_inserter(filtered), [](int i) { return i % 3 == 0; }); std::for_each(filtered.begin(), filtered.end(), [](int i) { std::cout << i << '\n'; });
This works, but it's far less concise or fluent than Groovy's syntax.
Existing Libraries for Fluent Operations
If you don't want to roll your own, there are mature third-party libraries that add exactly this kind of chained, Groovy-like functionality:
1. Boost.Range
Boost.Range is a widely used library that extends the standard library with range-based operations, including chained adapters. Your example would look like this:
#include <boost/range/adaptors.hpp> #include <boost/range/algorithm.hpp> #include <vector> #include <iostream> using namespace boost::adaptors; using namespace boost::range; // Equivalent of ["a", "b"].each std::vector<std::string> strs = {"a", "b"}; for_each(strs, [](const auto& s) { std::cout << s << '\n'; }); // Equivalent of [1,2].collect(...).grep(...).each std::vector<int> nums = {1, 2}; for_each( nums | transformed([](int i) { return i * 2; }) | filtered([](int i) { return i % 3 == 0; }), [](int i) { std::cout << i << '\n'; } );
It uses the | operator to chain adapters (transformed = Groovy's collect, filtered = Groovy's grep) and keeps the code concise.
2. Range-v3
Range-v3 is the precursor to C20's standard ranges library, and it works perfectly in C11/14. It supports lazy evaluation (no intermediate containers unless you want them) and has a very natural fluent syntax:
#include <range/v3/all.hpp> #include <vector> #include <iostream> using namespace ranges; // Equivalent of ["a", "b"].each std::vector<std::string> strs = {"a", "b"}; strs | actions::for_each([](const auto& s) { std::cout << s << '\n'; }); // Equivalent of [1,2].collect(...).grep(...).each std::vector<int> nums = {1, 2}; nums | views::transform([](int i) { return i * 2; }) | views::filter([](int i) { return i % 3 == 0; }) | actions::for_each([](int i) { std::cout << i << '\n'; });
This is almost identical to the syntax you envisioned, just using | instead of dot notation for chaining.
Rolling Your Own Custom Collection Class
If you want full control over the syntax (matching your exact Collection({...}).Each(...) idea), you can easily implement a wrapper class. Here's a minimal working example:
#include <vector> #include <functional> #include <iostream> template<typename T> class Collection { private: std::vector<T> data; public: // Initialize from initializer list Collection(std::initializer_list<T> init) : data(init) {} // Initialize from existing vector Collection(std::vector<T> vec) : data(std::move(vec)) {} // Each: Iterate and apply a function Collection& Each(std::function<void(const T&)> func) { for (const auto& elem : data) { func(elem); } return *this; } // Collect: Transform elements into a new Collection template<typename U> Collection<U> Collect(std::function<U(const T&)> func) const { std::vector<U> new_data; new_data.reserve(data.size()); for (const auto& elem : data) { new_data.push_back(func(elem)); } return Collection<U>(std::move(new_data)); } // Grep: Filter elements into a new Collection Collection Grep(std::function<bool(const T&)> func) const { std::vector<T> new_data; for (const auto& elem : data) { if (func(elem)) { new_data.push_back(elem); } } return Collection(std::move(new_data)); } }; // Test your exact desired syntax int main() { Collection<std::string>{"a", "b"}.Each([](const auto& s) { std::cout << s << '\n'; }); Collection<int>{1, 2} .Collect([](int i) { return i * 2; }) .Grep([](int i) { return i % 3 == 0; }) .Each([](int i) { std::cout << i << '\n'; }); return 0; }
This implementation matches your proposed syntax exactly. You can extend it further (add a Range class, support lazy evaluation, or handle more container types) based on your needs.
Final Takeaway
- C++11/14 standard library doesn't have native fluent collection operations, but you can use standalone algorithms.
- Third-party libraries like Boost.Range or Range-v3 provide battle-tested, efficient implementations of the syntax you want.
- Writing your own
Collectionwrapper is straightforward if you need full control over the API.
内容的提问来源于stack exchange,提问作者dromodel

