Skip to content

Commit da33b53

Browse files
author
Ryan Haining
committed
Forwards key to Group
If we knew the key type was not a reference, we could just call std::move, but there might be an lvalue reference hiding in there.
1 parent ed8b842 commit da33b53

File tree

2 files changed

+43
-2
lines changed

2 files changed

+43
-2
lines changed

cppitertools/groupby.hpp

+6-2
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,8 @@ class iter::impl::GroupProducer {
178178
friend class Iterator;
179179
friend class GroupIterator;
180180
Iterator<ContainerT>& owner_;
181+
// The key function may return a reference, so we need to call forward, not
182+
// move, when going for efficiency.
181183
key_func_ret<ContainerT> key_;
182184

183185
// completed is set if a Group is iterated through
@@ -192,7 +194,7 @@ class iter::impl::GroupProducer {
192194
bool completed = false;
193195

194196
Group(Iterator<ContainerT>& owner, key_func_ret<ContainerT> key)
195-
: owner_(owner), key_(key) {}
197+
: owner_(owner), key_(std::forward<key_func_ret<ContainerT>>(key)) {}
196198

197199
public:
198200
~Group() {
@@ -204,7 +206,9 @@ class iter::impl::GroupProducer {
204206

205207
// move-constructible, non-copy-constructible, non-assignable
206208
Group(Group&& other) noexcept
207-
: owner_(other.owner_), key_{other.key_}, completed{other.completed} {
209+
: owner_(other.owner_),
210+
key_{std::forward<key_func_ret<ContainerT>>(other.key_)},
211+
completed{other.completed} {
208212
other.completed = true;
209213
}
210214

test/test_groupby.cpp

+37
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,43 @@ namespace {
4040

4141
const std::vector<std::string> vec = {
4242
"hi", "ab", "ho", "abc", "def", "abcde", "efghi"};
43+
44+
struct Person {
45+
std::string name;
46+
int id;
47+
bool operator==(const Person& other) const {
48+
return id == other.id;
49+
}
50+
};
51+
52+
std::string& get_name(Person& p) {
53+
return p.name;
54+
}
55+
56+
template <typename G>
57+
std::vector<Person> extract_person_group(G g) {
58+
return {std::begin(g), std::end(g)};
59+
}
60+
}
61+
62+
TEST_CASE("groupby: handle key function that returns reference", "[groupby]") {
63+
std::vector<Person> people = {{"first", 1}, {"first", 2}, {"first", 3}};
64+
std::vector<std::string> keys;
65+
std::vector<std::vector<Person>> groups;
66+
67+
for (auto&& gb : groupby(people, get_name)) {
68+
groups.push_back(extract_person_group(std::move(gb.second)));
69+
keys.push_back(gb.first);
70+
}
71+
72+
const std::vector<std::string> kc = {"first"};
73+
const std::vector<std::vector<Person>> gc = {
74+
{{"first", 1}, {"first", 2}, {"first", 3}}};
75+
76+
REQUIRE(people[0].name == "first");
77+
REQUIRE(gc[0][0].name == "first");
78+
REQUIRE(keys == kc);
79+
REQUIRE(groups == gc);
4380
}
4481

4582
TEST_CASE("groupby: handles different callable types", "[groupby]") {

0 commit comments

Comments
 (0)