GCC Code Coverage Report


Directory: ./
File: TestZip.cpp
Date: 2026-03-21 20:51:59
Exec Total Coverage
Lines: 132 132 100.0%
Functions: 23 23 100.0%
Branches: 234 553 42.3%

Line Branch Exec Source
1 #include "Verbose.hpp"
2 #include "compat/ranges23.hpp"
3 #include <gtest/gtest.h>
4 #include <tuple>
5
6 class Zip : public testing::Test {
7 public:
8 // NOLINTBEGIN(*-avoid-magic-numbers)
9
10 6 void SetUp() override {
11 6 left = {1, 2, 3, 4, 5};
12 6 right = {1.1, 2.2, 3.3, 4.4, 5.5};
13
14 // Carefully avoid copies and moves.
15 6 center.reserve(5);
16
2/2
✓ Branch 7 taken 30 times.
✓ Branch 8 taken 6 times.
36 for (const auto& name : {"one", "two", "three", "four", "five"}) {
17
1/2
✓ Branch 4 taken 30 times.
✗ Branch 5 not taken.
30 center.emplace_back(name);
18 }
19 6 }
20
21 // NOLINTEND(*-avoid-magic-numbers)
22
23 template <typename T>
24 16 void print(const std::string& label, const T& range) {
25 16 std::cout << label;
26
15/23
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
✓ Branch 8 taken 10 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 10 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 30 times.
✓ Branch 13 taken 5 times.
✓ Branch 14 taken 5 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 5 times.
✓ Branch 17 taken 6 times.
✓ Branch 18 taken 6 times.
✓ Branch 19 taken 26 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 25 times.
✗ Branch 23 not taken.
✓ Branch 25 taken 25 times.
✗ Branch 26 not taken.
✓ Branch 28 taken 30 times.
✗ Branch 29 not taken.
✓ Branch 30 taken 25 times.
✓ Branch 31 taken 5 times.
156 for (const auto& element : range) { std::cout << element << " "; }
27 16 std::cout << std::endl;
28 16 }
29
30 std::vector<int> left;
31 std::vector<Verbose<>> center;
32 std::vector<double> right;
33 };
34
35 8 TEST_F(Zip, print) {
36
2/4
✓ Branch 10 taken 1 times.
✗ Branch 11 not taken.
✓ Branch 14 taken 1 times.
✗ Branch 15 not taken.
6 print("left: ", left);
37
2/4
✓ Branch 10 taken 1 times.
✗ Branch 11 not taken.
✓ Branch 14 taken 1 times.
✗ Branch 15 not taken.
6 print("center: ", center);
38
2/4
✓ Branch 10 taken 1 times.
✗ Branch 11 not taken.
✓ Branch 14 taken 1 times.
✗ Branch 15 not taken.
6 print("right: ", right);
39
2/4
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✓ Branch 11 taken 1 times.
✗ Branch 12 not taken.
8 print("zip(left,center,right): ",
40
1/2
✓ Branch 11 taken 1 times.
✗ Branch 12 not taken.
4 std::ranges::views::zip(left, center, right));
41 2 }
42
43 #if __cplusplus >= 201703L // since C++17
44
45 8 TEST_F(Zip, for_mutable) {
46
3/6
✓ Branch 15 taken 1 times.
✗ Branch 16 not taken.
✓ Branch 21 taken 1 times.
✗ Branch 22 not taken.
✓ Branch 25 taken 1 times.
✗ Branch 26 not taken.
6 print("before: ", std::ranges::views::zip(left, center, right));
47
48 // When iterating over a single sequence, convention dictates that we use
49 // a non-const lvalue reference. The presence of the reference makes it
50 // clear that no copy is being made. The absence of the const keyword makes
51 // it clear that the elements can be modified.
52
3/4
✓ Branch 13 taken 5 times.
✗ Branch 14 not taken.
✓ Branch 17 taken 5 times.
✓ Branch 18 taken 1 times.
12 for (auto& a : center) { a.name().append("?"); }
53
54 // Because each element of a zipped sequence is a temporary tuple,
55 // we cannot use a non-const lvalue reference in the for loop, but we can
56 // safely make a copy. Because the tuple contains references, we are not
57 // making copies of the underlying sequence elements themselves. However,
58 // the absence of a reference may be confusing. It looks like we are making
59 // copies and that any modifications affect only the copies, which would
60 // probably be a bug.
61
11/20
✓ Branch 11 taken 1 times.
✗ Branch 12 not taken.
✓ Branch 16 taken 1 times.
✗ Branch 17 not taken.
✓ Branch 21 taken 1 times.
✗ Branch 22 not taken.
✓ Branch 25 taken 5 times.
✗ Branch 26 not taken.
✓ Branch 28 taken 5 times.
✗ Branch 29 not taken.
✓ Branch 32 taken 5 times.
✗ Branch 33 not taken.
✓ Branch 36 taken 5 times.
✗ Branch 37 not taken.
✓ Branch 40 taken 5 times.
✗ Branch 41 not taken.
✓ Branch 43 taken 6 times.
✗ Branch 44 not taken.
✓ Branch 45 taken 5 times.
✓ Branch 46 taken 1 times.
12 for (auto [a, b, c] : std::ranges::views::zip(left, center, right)) {
62 // Verify previous modifications.
63
2/10
✓ Branch 8 taken 5 times.
✗ Branch 9 not taken.
✗ Branch 12 not taken.
✓ Branch 13 taken 5 times.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
10 EXPECT_EQ('?', b.name().back());
64 // Make additional modifications.
65 10 c -= a++;
66
1/2
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
10 b.name().append("!");
67 2 }
68
69 // We could use a const lvalue reference, but the presence of the const
70 // keyword may be confusing. It looks like we should not be able to modify
71 // the sequence elements.
72
11/20
✓ Branch 11 taken 1 times.
✗ Branch 12 not taken.
✓ Branch 16 taken 1 times.
✗ Branch 17 not taken.
✓ Branch 21 taken 1 times.
✗ Branch 22 not taken.
✓ Branch 25 taken 5 times.
✗ Branch 26 not taken.
✓ Branch 31 taken 5 times.
✗ Branch 32 not taken.
✓ Branch 38 taken 5 times.
✗ Branch 39 not taken.
✓ Branch 45 taken 5 times.
✗ Branch 46 not taken.
✓ Branch 49 taken 5 times.
✗ Branch 50 not taken.
✓ Branch 52 taken 6 times.
✗ Branch 53 not taken.
✓ Branch 54 taken 5 times.
✓ Branch 55 taken 1 times.
14 for (const auto& [a, b, c] : std::ranges::views::zip(left, center, right)) {
73 // Verify previous modifications.
74
2/10
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 5 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
10 EXPECT_FLOAT_EQ(0.1 * (a - 1), c);
75
2/10
✓ Branch 8 taken 5 times.
✗ Branch 9 not taken.
✗ Branch 12 not taken.
✓ Branch 13 taken 5 times.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
10 EXPECT_EQ('!', b.name().back());
76 // Make additional modifications.
77 10 c = 1.1 * a++; // NOLINT(*-avoid-magic-numbers)
78 10 b.name().pop_back();
79 2 }
80
81 // The least confusing solution uses a universal reference.
82 // The presence of the reference makes it clear that no copy is being made.
83 // The absence of the const keyword makes it clear that the elements can be
84 // modified.
85
11/20
✓ Branch 11 taken 1 times.
✗ Branch 12 not taken.
✓ Branch 16 taken 1 times.
✗ Branch 17 not taken.
✓ Branch 21 taken 1 times.
✗ Branch 22 not taken.
✓ Branch 25 taken 5 times.
✗ Branch 26 not taken.
✓ Branch 30 taken 5 times.
✗ Branch 31 not taken.
✓ Branch 36 taken 5 times.
✗ Branch 37 not taken.
✓ Branch 42 taken 5 times.
✗ Branch 43 not taken.
✓ Branch 46 taken 5 times.
✗ Branch 47 not taken.
✓ Branch 49 taken 6 times.
✗ Branch 50 not taken.
✓ Branch 51 taken 5 times.
✓ Branch 52 taken 1 times.
14 for (auto&& [a, b, c] : std::ranges::views::zip(left, center, right)) {
86 // Verify previous modifications.
87
2/10
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 5 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
10 EXPECT_FLOAT_EQ(1.1 * (a - 1), c);
88
2/10
✓ Branch 8 taken 5 times.
✗ Branch 9 not taken.
✗ Branch 12 not taken.
✓ Branch 13 taken 5 times.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
10 EXPECT_EQ('?', b.name().back());
89 // Make additional modifications.
90 10 a -= 2;
91 10 c -= 1.1; // NOLINT(*-avoid-magic-numbers)
92 10 b.name().pop_back();
93 2 }
94
95 // Nothing stops us from using a universal reference when iterating over a
96 // single sequence, though it may look unconventional.
97
2/2
✓ Branch 11 taken 5 times.
✓ Branch 12 taken 1 times.
12 for (auto&& a : center) {
98
1/2
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
10 a.name().append("*");
99 10 a.name().pop_back();
100 2 }
101
102
3/6
✓ Branch 15 taken 1 times.
✗ Branch 16 not taken.
✓ Branch 21 taken 1 times.
✗ Branch 22 not taken.
✓ Branch 25 taken 1 times.
✗ Branch 26 not taken.
6 print("after: ", std::ranges::views::zip(left, center, right));
103 2 }
104
105 #endif // C++17
106
107 8 TEST_F(Zip, for_mutable_tuple) {
108
3/6
✓ Branch 15 taken 1 times.
✗ Branch 16 not taken.
✓ Branch 21 taken 1 times.
✗ Branch 22 not taken.
✓ Branch 25 taken 1 times.
✗ Branch 26 not taken.
6 print("before: ", std::ranges::views::zip(left, center, right));
109
110 // We can use a tuple instead of structured binding. We cannot use a
111 // non-const lvalue reference, so we use a universal reference to
112 // minimize confusion. (See discussion above.)
113
8/14
✓ Branch 11 taken 1 times.
✗ Branch 12 not taken.
✓ Branch 16 taken 1 times.
✗ Branch 17 not taken.
✓ Branch 21 taken 1 times.
✗ Branch 22 not taken.
✓ Branch 25 taken 5 times.
✗ Branch 26 not taken.
✓ Branch 28 taken 5 times.
✗ Branch 29 not taken.
✓ Branch 31 taken 6 times.
✗ Branch 32 not taken.
✓ Branch 33 taken 5 times.
✓ Branch 34 taken 1 times.
14 for (auto&& tuple : std::ranges::views::zip(left, center, right)) {
114
1/2
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
10 auto& a = std::get<0>(tuple);
115
1/2
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
10 auto& b = std::get<1>(tuple);
116
1/2
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
10 auto& c = std::get<2>(tuple);
117 10 c -= a++;
118
1/2
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
10 b.name().append("!");
119 2 }
120
8/14
✓ Branch 11 taken 1 times.
✗ Branch 12 not taken.
✓ Branch 16 taken 1 times.
✗ Branch 17 not taken.
✓ Branch 21 taken 1 times.
✗ Branch 22 not taken.
✓ Branch 25 taken 5 times.
✗ Branch 26 not taken.
✓ Branch 28 taken 5 times.
✗ Branch 29 not taken.
✓ Branch 31 taken 6 times.
✗ Branch 32 not taken.
✓ Branch 33 taken 5 times.
✓ Branch 34 taken 1 times.
14 for (auto&& tuple : std::ranges::views::zip(left, center, right)) {
121
1/2
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
10 auto& a = std::get<0>(tuple);
122
1/2
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
10 auto& b = std::get<1>(tuple);
123
1/2
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
10 auto& c = std::get<2>(tuple);
124 // Verify previous modifications.
125
2/10
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 5 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
10 EXPECT_FLOAT_EQ(0.1 * (a - 1), c);
126
2/10
✓ Branch 8 taken 5 times.
✗ Branch 9 not taken.
✗ Branch 12 not taken.
✓ Branch 13 taken 5 times.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
10 EXPECT_EQ('!', b.name().back());
127 // Put things back the way we found them.
128 10 c += --a;
129 10 b.name().pop_back();
130 2 }
131
132
3/6
✓ Branch 15 taken 1 times.
✗ Branch 16 not taken.
✓ Branch 21 taken 1 times.
✗ Branch 22 not taken.
✓ Branch 25 taken 1 times.
✗ Branch 26 not taken.
6 print("after: ", std::ranges::views::zip(left, center, right));
133 2 }
134
135 8 TEST_F(Zip, for_const) {
136 // When iterating over a single sequence, convention dictates that we use
137 // a const lvalue reference when we do not intend to modify the elements.
138 // The presence of the reference makes it clear that no copy is being made.
139 // The presence of the const keyword makes it clear that the elements cannot
140 // be modified.
141
2/2
✓ Branch 11 taken 5 times.
✓ Branch 12 taken 1 times.
12 for (const auto& a : center) {
142 // a.name().append("!"); // compile time error
143
2/10
✓ Branch 8 taken 5 times.
✗ Branch 9 not taken.
✗ Branch 12 not taken.
✓ Branch 13 taken 5 times.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
10 EXPECT_NE('!', a.name().back());
144 2 }
145
146 // When the sequence itself is const, the const keyword becomes optional,
147 // but we can use it anyway to make our intentions clear. This also guards
148 // against future changes in the declaration of the sequence.
149 2 const auto& ccenter = center;
150
2/2
✓ Branch 8 taken 5 times.
✓ Branch 9 taken 1 times.
12 for (const auto& a : ccenter) {
151 // a.name().append("!"); // compile time error
152
2/10
✓ Branch 8 taken 5 times.
✗ Branch 9 not taken.
✗ Branch 12 not taken.
✓ Branch 13 taken 5 times.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
10 EXPECT_NE('!', a.name().back());
153 2 }
154
155 #if __cplusplus >= 201703L // since C++17
156
157 // To make our intentions more obvious, we can explicitly make the sequence const.
158
2/2
✓ Branch 13 taken 5 times.
✓ Branch 14 taken 1 times.
12 for (const auto& a : std::as_const(center)) {
159 // a.name().append("!"); // compile time error
160
2/10
✓ Branch 8 taken 5 times.
✗ Branch 9 not taken.
✗ Branch 12 not taken.
✓ Branch 13 taken 5 times.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
10 EXPECT_NE('!', a.name().back());
161 2 }
162
163 // Unfortunately using a const lvalue reference does not have the intended
164 // effect when iterating over a zipped sequence. This is because each tuple
165 // in the zipped sequence contains non-const references.
166 // The tuple may be const, but the elements can still be modified!
167
11/20
✓ Branch 11 taken 1 times.
✗ Branch 12 not taken.
✓ Branch 16 taken 1 times.
✗ Branch 17 not taken.
✓ Branch 21 taken 1 times.
✗ Branch 22 not taken.
✓ Branch 25 taken 5 times.
✗ Branch 26 not taken.
✓ Branch 31 taken 5 times.
✗ Branch 32 not taken.
✓ Branch 38 taken 5 times.
✗ Branch 39 not taken.
✓ Branch 45 taken 5 times.
✗ Branch 46 not taken.
✓ Branch 49 taken 5 times.
✗ Branch 50 not taken.
✓ Branch 52 taken 6 times.
✗ Branch 53 not taken.
✓ Branch 54 taken 5 times.
✓ Branch 55 taken 1 times.
22 for (const auto& [a, b, c] : std::ranges::views::zip(left, center, right)) {
168 10 c -= a++; // surprise!
169
1/2
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
10 b.name().append("!"); // surprise!
170 2 }
171
172 // Instead, we must ensure that the underlying sequences being zipped are
173 // const. The const keyword becomes optional, but we can use it anyway
174 // to make our intentions clear.
175 2 const auto& cleft = left;
176 2 const auto& cright = right;
177
11/20
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✓ Branch 12 taken 1 times.
✗ Branch 13 not taken.
✓ Branch 16 taken 5 times.
✗ Branch 17 not taken.
✓ Branch 22 taken 5 times.
✗ Branch 23 not taken.
✓ Branch 29 taken 5 times.
✗ Branch 30 not taken.
✓ Branch 36 taken 5 times.
✗ Branch 37 not taken.
✓ Branch 40 taken 5 times.
✗ Branch 41 not taken.
✓ Branch 43 taken 6 times.
✗ Branch 44 not taken.
✓ Branch 45 taken 5 times.
✓ Branch 46 taken 1 times.
24 for (const auto& [a, b, c] : std::ranges::views::zip(cleft, ccenter, cright)) {
178
2/10
✓ Branch 8 taken 5 times.
✗ Branch 9 not taken.
✗ Branch 12 not taken.
✓ Branch 13 taken 5 times.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
10 EXPECT_EQ('!', b.name().back());
179 // c -= a++; // compile time error
180 // b.name().append("!"); // compile time error
181 2 }
182
183 // To make our intentions more obvious, and to guard against future changes
184 // in the declarations of the sequences, we can explicitly make each
185 // sequence const before combining them.
186
3/6
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
✓ Branch 8 taken 5 times.
✗ Branch 9 not taken.
✓ Branch 13 taken 5 times.
✗ Branch 14 not taken.
32 for (const auto& [a, b, c] : std::ranges::views::zip(
187
8/14
✓ Branch 17 taken 1 times.
✗ Branch 18 not taken.
✓ Branch 22 taken 1 times.
✗ Branch 23 not taken.
✓ Branch 27 taken 1 times.
✗ Branch 28 not taken.
✓ Branch 31 taken 5 times.
✗ Branch 32 not taken.
✓ Branch 40 taken 5 times.
✗ Branch 41 not taken.
✓ Branch 43 taken 6 times.
✗ Branch 44 not taken.
✓ Branch 45 taken 5 times.
✓ Branch 46 taken 1 times.
56 std::as_const(left), std::as_const(center), std::as_const(right))) {
188
2/10
✓ Branch 8 taken 5 times.
✗ Branch 9 not taken.
✗ Branch 12 not taken.
✓ Branch 13 taken 5 times.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
10 EXPECT_EQ('!', b.name().back());
189 // c -= a++; // compile time error
190 // b.name().append("!"); // compile time error
191 2 }
192
193 #endif // C++17
194
195 #if __cplusplus >= 202302L // since C++23
196
197 // To make our intentions more obvious, and to guard against future changes
198 // in the declarations of the sequences, we can explicitly make each
199 // sequence const before combining them.
200 for (const auto& [a, b, c] :
201 std::ranges::views::zip(left, center, right) | std::views::as_const) {
202 EXPECT_EQ('!', b.name().back());
203 // c -= a++; // compile time error
204 // b.name().append("!"); // compile time error
205 }
206
207 #endif // C++23
208 2 }
209
210 8 TEST_F(Zip, for_const_tuple) {
211 // We can use a tuple instead of structured binding. The const keyword is
212 // optional as it has no effect. This is because each tuple in the zipped
213 // sequence contains non-const references. The tuple may be const, but the
214 // elements could still be modified! Using const lvalue references as we
215 // unpack the tuple makes it clear that the elements cannot be modified.
216
8/14
✓ Branch 11 taken 1 times.
✗ Branch 12 not taken.
✓ Branch 16 taken 1 times.
✗ Branch 17 not taken.
✓ Branch 21 taken 1 times.
✗ Branch 22 not taken.
✓ Branch 25 taken 5 times.
✗ Branch 26 not taken.
✓ Branch 28 taken 5 times.
✗ Branch 29 not taken.
✓ Branch 31 taken 6 times.
✗ Branch 32 not taken.
✓ Branch 33 taken 5 times.
✓ Branch 34 taken 1 times.
24 for (const auto& tuple : std::ranges::views::zip(left, center, right)) {
217
1/2
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
10 const auto& a = std::get<0>(tuple);
218
1/2
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
10 const auto& b = std::get<1>(tuple);
219
1/2
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
10 const auto& c = std::get<2>(tuple);
220
2/10
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 5 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
10 EXPECT_FLOAT_EQ(1.1 * a, c);
221
2/10
✓ Branch 8 taken 5 times.
✗ Branch 9 not taken.
✗ Branch 12 not taken.
✓ Branch 13 taken 5 times.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
10 EXPECT_NE('!', b.name().back());
222 // b.name().append("!"); // compile time error
223 2 }
224 2 }
225
226 8 TEST_F(Zip, for_copy) {
227 // When iterating over a single sequence, convention dictates that we
228 // avoid using a reference when we intend to make copies of the elements.
229 // The absence of a reference indicates that a copy is being made.
230 // The absence of the const keyword indicates that the copies can be modified.
231
3/4
✓ Branch 11 taken 5 times.
✗ Branch 12 not taken.
✓ Branch 15 taken 5 times.
✓ Branch 16 taken 1 times.
12 for (auto a : center) {
232
1/2
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
10 a.name().append("!");
233
2/10
✓ Branch 7 taken 5 times.
✗ Branch 8 not taken.
✗ Branch 11 not taken.
✓ Branch 12 taken 5 times.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
10 EXPECT_EQ('!', a.name().back()); // Verify that copy is modified.
234 12 }
235
2/2
✓ Branch 11 taken 5 times.
✓ Branch 12 taken 1 times.
12 for (const auto& a : center) {
236
2/10
✓ Branch 8 taken 5 times.
✗ Branch 9 not taken.
✗ Branch 12 not taken.
✓ Branch 13 taken 5 times.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
10 EXPECT_NE('!', a.name().back()); // Verify that original is not modified.
237 2 }
238
239 #if __cplusplus >= 201703L // since C++17
240
241 // Unfortunately using the same convention with a zipped sequence does not
242 // have the intended effect. This is because each tuple in the zipped
243 // sequence contains non-const references. The tuple may have been copied,
244 // but the original elements can still be modified!
245
11/20
✓ Branch 11 taken 1 times.
✗ Branch 12 not taken.
✓ Branch 16 taken 1 times.
✗ Branch 17 not taken.
✓ Branch 21 taken 1 times.
✗ Branch 22 not taken.
✓ Branch 25 taken 5 times.
✗ Branch 26 not taken.
✓ Branch 28 taken 5 times.
✗ Branch 29 not taken.
✓ Branch 32 taken 5 times.
✗ Branch 33 not taken.
✓ Branch 36 taken 5 times.
✗ Branch 37 not taken.
✓ Branch 40 taken 5 times.
✗ Branch 41 not taken.
✓ Branch 43 taken 6 times.
✗ Branch 44 not taken.
✓ Branch 45 taken 5 times.
✓ Branch 46 taken 1 times.
24 for (auto [a, b, c] : std::ranges::views::zip(left, center, right)) {
246 10 c -= a++; // surprise!
247
1/2
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
10 b.name().append("!"); // surprise!
248 // Put things back the way we found them.
249 10 c += --a;
250 10 b.name().pop_back();
251 2 }
252
253 #endif // C++17
254
255 // The least confusing solution is to make copies as we unpack a tuple.
256
8/14
✓ Branch 11 taken 1 times.
✗ Branch 12 not taken.
✓ Branch 16 taken 1 times.
✗ Branch 17 not taken.
✓ Branch 21 taken 1 times.
✗ Branch 22 not taken.
✓ Branch 25 taken 5 times.
✗ Branch 26 not taken.
✓ Branch 28 taken 5 times.
✗ Branch 29 not taken.
✓ Branch 31 taken 6 times.
✗ Branch 32 not taken.
✓ Branch 33 taken 5 times.
✓ Branch 34 taken 1 times.
24 for (auto tuple : std::ranges::views::zip(left, center, right)) {
257
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
10 auto a = std::get<0>(tuple);
258
2/4
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 5 times.
✗ Branch 7 not taken.
10 auto b = std::get<1>(tuple);
259
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
10 auto c = std::get<2>(tuple);
260
1/10
✗ Branch 5 not taken.
✓ Branch 6 taken 5 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
10 EXPECT_FALSE(std::is_reference<decltype(a)>::value);
261
1/10
✗ Branch 5 not taken.
✓ Branch 6 taken 5 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
10 EXPECT_FALSE(std::is_reference<decltype(b)>::value);
262
1/10
✗ Branch 5 not taken.
✓ Branch 6 taken 5 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
10 EXPECT_FALSE(std::is_reference<decltype(c)>::value);
263 // Modify the copies.
264 10 c -= a++;
265
1/2
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
10 b.name().append("!");
266 12 }
267
8/14
✓ Branch 11 taken 1 times.
✗ Branch 12 not taken.
✓ Branch 16 taken 1 times.
✗ Branch 17 not taken.
✓ Branch 21 taken 1 times.
✗ Branch 22 not taken.
✓ Branch 25 taken 5 times.
✗ Branch 26 not taken.
✓ Branch 28 taken 5 times.
✗ Branch 29 not taken.
✓ Branch 31 taken 6 times.
✗ Branch 32 not taken.
✓ Branch 33 taken 5 times.
✓ Branch 34 taken 1 times.
24 for (const auto& tuple : std::ranges::views::zip(left, center, right)) {
268
1/2
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
10 const auto& a = std::get<0>(tuple);
269
1/2
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
10 const auto& b = std::get<1>(tuple);
270
1/2
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
10 const auto& c = std::get<2>(tuple);
271 // Verify that original values were not modified.
272
2/10
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 5 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
10 EXPECT_FLOAT_EQ(1.1 * a, c);
273
2/10
✓ Branch 8 taken 5 times.
✗ Branch 9 not taken.
✗ Branch 12 not taken.
✓ Branch 13 taken 5 times.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
10 EXPECT_NE('!', b.name().back());
274 2 }
275 2 }
276