1 | /* |
2 | * Copyright 2012-present Facebook, Inc. |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at |
7 | * |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * |
10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. |
15 | */ |
16 | |
17 | #include <iostream> |
18 | |
19 | #include <folly/Overload.h> |
20 | #include <folly/functional/ApplyTuple.h> |
21 | #include <folly/portability/GTest.h> |
22 | |
23 | #include <array> |
24 | #include <memory> |
25 | |
26 | namespace { |
27 | |
28 | void func(int a, int b, double c) { |
29 | EXPECT_EQ(a, 1); |
30 | EXPECT_EQ(b, 2); |
31 | EXPECT_EQ(c, 3.0); |
32 | } |
33 | |
34 | struct Wat { |
35 | void func(int a, int b, double c) { |
36 | ::func(a, b, c); |
37 | } |
38 | |
39 | double retVal(int a, double b) { |
40 | return a + b; |
41 | } |
42 | |
43 | Wat() {} |
44 | Wat(Wat const&) = delete; |
45 | |
46 | int foo; |
47 | }; |
48 | |
49 | struct Overloaded { |
50 | int func(int) { |
51 | return 0; |
52 | } |
53 | bool func(bool) { |
54 | return true; |
55 | } |
56 | }; |
57 | |
58 | struct Func { |
59 | int operator()() const { |
60 | return 1; |
61 | } |
62 | }; |
63 | |
64 | struct CopyCount { |
65 | CopyCount() {} |
66 | CopyCount(CopyCount const&) { |
67 | std::cout << "copy count copy ctor\n" ; |
68 | } |
69 | }; |
70 | |
71 | void anotherFunc(CopyCount const&) {} |
72 | |
73 | std::function<void(int, int, double)> makeFunc() { |
74 | return &func; |
75 | } |
76 | |
77 | struct GuardObjBase { |
78 | GuardObjBase(GuardObjBase&&) noexcept {} |
79 | GuardObjBase() {} |
80 | GuardObjBase(GuardObjBase const&) = delete; |
81 | GuardObjBase& operator=(GuardObjBase const&) = delete; |
82 | }; |
83 | typedef GuardObjBase const& Guard; |
84 | |
85 | template <class F, class Tuple> |
86 | struct GuardObj : GuardObjBase { |
87 | explicit GuardObj(F&& f, Tuple&& args) |
88 | : f_(std::forward<F>(f)), args_(std::forward<Tuple>(args)) {} |
89 | GuardObj(GuardObj&& g) noexcept |
90 | : GuardObjBase(std::move(g)), |
91 | f_(std::move(g.f_)), |
92 | args_(std::move(g.args_)) {} |
93 | |
94 | ~GuardObj() { |
95 | folly::apply(f_, args_); |
96 | } |
97 | |
98 | GuardObj(const GuardObj&) = delete; |
99 | GuardObj& operator=(const GuardObj&) = delete; |
100 | |
101 | private: |
102 | F f_; |
103 | Tuple args_; |
104 | }; |
105 | |
106 | template <class F, class... Args> |
107 | GuardObj<typename std::decay<F>::type, std::tuple<Args...>> guard( |
108 | F&& f, |
109 | Args&&... args) { |
110 | return GuardObj<typename std::decay<F>::type, std::tuple<Args...>>( |
111 | std::forward<F>(f), std::tuple<Args...>(std::forward<Args>(args)...)); |
112 | } |
113 | |
114 | struct Mover { |
115 | Mover() {} |
116 | Mover(Mover&&) noexcept {} |
117 | Mover(const Mover&) = delete; |
118 | Mover& operator=(const Mover&) = delete; |
119 | }; |
120 | |
121 | void move_only_func(Mover&&) {} |
122 | |
123 | } // namespace |
124 | |
125 | TEST(ApplyTuple, Test) { |
126 | auto argsTuple = std::make_tuple(1, 2, 3.0); |
127 | auto func2 = func; |
128 | folly::apply(func2, argsTuple); |
129 | folly::apply(func, argsTuple); |
130 | folly::apply(func, std::make_tuple(1, 2, 3.0)); |
131 | folly::apply(makeFunc(), std::make_tuple(1, 2, 3.0)); |
132 | folly::apply(makeFunc(), argsTuple); |
133 | |
134 | std::unique_ptr<Wat> wat(new Wat); |
135 | folly::apply(&Wat::func, std::make_tuple(wat.get(), 1, 2, 3.0)); |
136 | auto argsTuple2 = std::make_tuple(wat.get(), 1, 2, 3.0); |
137 | folly::apply(&Wat::func, argsTuple2); |
138 | |
139 | EXPECT_EQ( |
140 | 10.0, folly::apply(&Wat::retVal, std::make_tuple(wat.get(), 1, 9.0))); |
141 | |
142 | auto test = guard(func, 1, 2, 3.0); |
143 | CopyCount cpy; |
144 | auto test2 = guard(anotherFunc, cpy); |
145 | auto test3 = guard(anotherFunc, std::cref(cpy)); |
146 | |
147 | Overloaded ovl; |
148 | EXPECT_EQ( |
149 | 0, |
150 | folly::apply( |
151 | static_cast<int (Overloaded::*)(int)>(&Overloaded::func), |
152 | std::make_tuple(&ovl, 12))); |
153 | EXPECT_EQ( |
154 | /* do not code-mode to EXPECT_TRUE */ true, |
155 | folly::apply( |
156 | static_cast<bool (Overloaded::*)(bool)>(&Overloaded::func), |
157 | std::make_tuple(&ovl, false))); |
158 | |
159 | int x = folly::apply(std::plus<int>(), std::make_tuple(12, 12)); |
160 | EXPECT_EQ(24, x); |
161 | |
162 | Mover m; |
163 | folly::apply( |
164 | move_only_func, std::forward_as_tuple(std::forward<Mover>(Mover()))); |
165 | const auto tuple3 = std::make_tuple(1, 2, 3.0); |
166 | folly::apply(func, tuple3); |
167 | } |
168 | |
169 | TEST(ApplyTuple, Mutable) { |
170 | auto argsTuple = std::make_tuple(1, 2, 3.0); |
171 | |
172 | folly::apply( |
173 | [](int a, int b, double c) mutable { func(a, b, c); }, argsTuple); |
174 | } |
175 | |
176 | TEST(ApplyTuple, ConstOverloads) { |
177 | struct ConstOverloaded { |
178 | ConstOverloaded() {} |
179 | int operator()() { |
180 | return 101; |
181 | } |
182 | int operator()() const { |
183 | return 102; |
184 | } |
185 | }; |
186 | |
187 | ConstOverloaded covl; |
188 | |
189 | // call operator()() |
190 | EXPECT_EQ(folly::apply(covl, std::make_tuple()), 101); |
191 | EXPECT_EQ(folly::apply(std::ref(covl), std::make_tuple()), 101); |
192 | EXPECT_EQ(folly::apply(std::move(covl), std::make_tuple()), 101); |
193 | |
194 | // call operator()() const |
195 | EXPECT_EQ( |
196 | folly::apply(const_cast<ConstOverloaded const&>(covl), std::make_tuple()), |
197 | 102); |
198 | EXPECT_EQ(folly::apply(std::cref(covl), std::make_tuple()), 102); |
199 | } |
200 | |
201 | TEST(ApplyTuple, RefOverloads) { |
202 | struct RefOverloaded { |
203 | RefOverloaded() {} |
204 | int operator()() & { |
205 | return 201; |
206 | } |
207 | int operator()() const& { |
208 | return 202; |
209 | } |
210 | int operator()() && { |
211 | return 203; |
212 | } |
213 | }; |
214 | |
215 | RefOverloaded rovl; |
216 | |
217 | // call operator()() & |
218 | EXPECT_EQ(folly::apply(rovl, std::make_tuple()), 201); |
219 | EXPECT_EQ(folly::apply(std::ref(rovl), std::make_tuple()), 201); |
220 | |
221 | // call operator()() const & |
222 | EXPECT_EQ( |
223 | folly::apply(const_cast<RefOverloaded const&>(rovl), std::make_tuple()), |
224 | 202); |
225 | EXPECT_EQ(folly::apply(std::cref(rovl), std::make_tuple()), 202); |
226 | |
227 | // call operator()() && |
228 | EXPECT_EQ(folly::apply(std::move(rovl), std::make_tuple()), 203); |
229 | } |
230 | |
231 | struct MemberFunc { |
232 | int x; |
233 | int getX() const { |
234 | return x; |
235 | } |
236 | void setX(int xx) { |
237 | x = xx; |
238 | } |
239 | }; |
240 | |
241 | TEST(ApplyTuple, MemberFunction) { |
242 | MemberFunc mf; |
243 | mf.x = 123; |
244 | |
245 | // call getter |
246 | EXPECT_EQ(folly::apply(&MemberFunc::getX, std::make_tuple(&mf)), 123); |
247 | |
248 | // call setter |
249 | folly::apply(&MemberFunc::setX, std::make_tuple(&mf, 234)); |
250 | EXPECT_EQ(mf.x, 234); |
251 | EXPECT_EQ(folly::apply(&MemberFunc::getX, std::make_tuple(&mf)), 234); |
252 | } |
253 | |
254 | TEST(ApplyTuple, MemberFunctionWithRefWrapper) { |
255 | MemberFunc mf; |
256 | mf.x = 234; |
257 | |
258 | EXPECT_EQ( |
259 | folly::apply(&MemberFunc::getX, std::make_tuple(std::ref(mf))), 234); |
260 | } |
261 | |
262 | TEST(ApplyTuple, MemberFunctionWithConstPointer) { |
263 | MemberFunc mf; |
264 | mf.x = 234; |
265 | |
266 | EXPECT_EQ( |
267 | folly::apply( |
268 | &MemberFunc::getX, |
269 | std::make_tuple(const_cast<MemberFunc const*>(&mf))), |
270 | 234); |
271 | } |
272 | |
273 | TEST(ApplyTuple, MemberFunctionWithSharedPtr) { |
274 | MemberFunc mf; |
275 | mf.x = 234; |
276 | |
277 | EXPECT_EQ( |
278 | folly::apply( |
279 | &MemberFunc::getX, std::make_tuple(std::make_shared<MemberFunc>(mf))), |
280 | 234); |
281 | } |
282 | |
283 | TEST(ApplyTuple, MemberFunctionWithUniquePtr) { |
284 | MemberFunc mf; |
285 | mf.x = 234; |
286 | |
287 | EXPECT_EQ( |
288 | folly::apply( |
289 | &MemberFunc::getX, std::make_tuple(std::make_unique<MemberFunc>(mf))), |
290 | 234); |
291 | } |
292 | |
293 | TEST(ApplyTuple, Array) { |
294 | folly::apply(func, std::array<int, 3>{{1, 2, 3}}); |
295 | folly::apply(func, std::array<double, 3>{{1, 2, 3}}); |
296 | } |
297 | |
298 | TEST(ApplyTuple, Pair) { |
299 | auto add = [](int x, int y) { return x + y; }; |
300 | |
301 | EXPECT_EQ(folly::apply(add, std::pair<int, int>{1200, 34}), 1234); |
302 | } |
303 | |
304 | TEST(ApplyTuple, MultipleTuples) { |
305 | auto add = [](int x, int y, int z) { return x * 100 + y * 10 + z; }; |
306 | |
307 | EXPECT_EQ(123, folly::apply(add, std::make_tuple(1, 2, 3))); |
308 | EXPECT_EQ( |
309 | 123, |
310 | folly::apply( |
311 | add, std::tuple_cat(std::make_tuple(1, 2, 3), std::make_tuple()))); |
312 | EXPECT_EQ( |
313 | 123, |
314 | folly::apply( |
315 | add, std::tuple_cat(std::make_tuple(1, 2), std::make_tuple(3)))); |
316 | EXPECT_EQ( |
317 | 123, |
318 | folly::apply( |
319 | add, std::tuple_cat(std::make_tuple(1), std::make_tuple(2, 3)))); |
320 | EXPECT_EQ( |
321 | 123, |
322 | folly::apply( |
323 | add, std::tuple_cat(std::make_tuple(), std::make_tuple(1, 2, 3)))); |
324 | |
325 | EXPECT_EQ( |
326 | 123, |
327 | folly::apply( |
328 | add, |
329 | std::tuple_cat( |
330 | std::make_tuple(1, 2, 3), std::make_tuple(), std::make_tuple()))); |
331 | EXPECT_EQ( |
332 | 123, |
333 | folly::apply( |
334 | add, |
335 | std::tuple_cat( |
336 | std::make_tuple(1), std::make_tuple(2), std::make_tuple(3)))); |
337 | EXPECT_EQ( |
338 | 123, |
339 | folly::apply( |
340 | add, |
341 | std::tuple_cat( |
342 | std::make_tuple(1), std::make_tuple(), std::make_tuple(2, 3)))); |
343 | } |
344 | |
345 | TEST(ApplyTuple, UncurryCopyMove) { |
346 | std::string separator = "================================\n" ; |
347 | auto formatRow = folly::uncurry([=](std::string a, std::string b) { |
348 | // capture separator by copy |
349 | return separator + a + "\n" + b + "\n" + separator; |
350 | }); |
351 | auto row = std::make_tuple("hello" , "world" ); |
352 | auto expected = separator + "hello\nworld\n" + separator; |
353 | EXPECT_EQ(expected, formatRow(row)); |
354 | auto formatRowCopy = formatRow; |
355 | EXPECT_EQ(expected, formatRowCopy(row)); |
356 | auto formatRowMove = std::move(formatRow); |
357 | EXPECT_EQ(expected, formatRowMove(row)); |
358 | |
359 | // capture value moved out from formatRow |
360 | EXPECT_NE(expected, formatRow(row)); |
361 | } |
362 | |
363 | TEST(ApplyTuple, Uncurry) { |
364 | EXPECT_EQ(42, folly::uncurry([](int x, int y) { |
365 | return x * y; |
366 | })(std::pair<int, int>(6, 7))); |
367 | EXPECT_EQ(42, folly::uncurry([](int&& x, int&& y) { |
368 | return x * y; |
369 | })(std::pair<int&&, int&&>(6, 7))); |
370 | EXPECT_EQ(42, folly::uncurry([](int&& x, int&& y) { |
371 | return x * y; |
372 | })(std::pair<int&&, int&&>(6, 7))); |
373 | |
374 | std::string long1 = "a long string exceeding small string size" ; |
375 | std::string long2 = "and here is another one!" ; |
376 | std::string expected = long1 + long2; |
377 | |
378 | auto cat = folly::uncurry( |
379 | [](std::string a, std::string b) { return std::move(a) + std::move(b); }); |
380 | |
381 | EXPECT_EQ(expected, cat(std::make_pair(long1, long2))); |
382 | EXPECT_FALSE(long1.empty()); |
383 | EXPECT_FALSE(long2.empty()); |
384 | EXPECT_EQ(expected, cat(std::tie(long1, long2))); |
385 | EXPECT_FALSE(long1.empty()); |
386 | EXPECT_FALSE(long2.empty()); |
387 | EXPECT_EQ( |
388 | expected, cat(std::forward_as_tuple(std::move(long1), std::move(long2)))); |
389 | EXPECT_TRUE(long1.empty()); |
390 | EXPECT_TRUE(long2.empty()); |
391 | } |
392 | |
393 | TEST(ApplyTuple, UncurryStdFind) { |
394 | std::vector<std::pair<int, int>> v{{1, 9}, {2, 8}, {3, 7}, {4, 6}, {5, 5}}; |
395 | EXPECT_EQ( |
396 | 3, std::count_if(v.begin(), v.end(), folly::uncurry([](int a, int b) { |
397 | return b % a == 0; |
398 | }))); |
399 | } |
400 | |
401 | namespace { |
402 | struct S { |
403 | template <typename... Args> |
404 | explicit S(Args&&... args) : tuple_(std::forward<Args>(args)...) {} |
405 | |
406 | std::tuple<int, double, std::string> tuple_; |
407 | }; |
408 | } // namespace |
409 | |
410 | TEST(MakeFromTupleTest, make_from_tuple) { |
411 | S expected{42, 1.0, "foobar" }; |
412 | |
413 | // const lvalue ref |
414 | auto s1 = folly::make_from_tuple<S>(expected.tuple_); |
415 | EXPECT_EQ(expected.tuple_, s1.tuple_); |
416 | |
417 | // rvalue ref |
418 | S sCopy{expected.tuple_}; |
419 | auto s2 = folly::make_from_tuple<S>(std::move(sCopy.tuple_)); |
420 | EXPECT_EQ(expected.tuple_, s2.tuple_); |
421 | EXPECT_TRUE(std::get<2>(sCopy.tuple_).empty()); |
422 | |
423 | // forward |
424 | std::string str{"foobar" }; |
425 | auto s3 = |
426 | folly::make_from_tuple<S>(std::forward_as_tuple(42, 1.0, std::move(str))); |
427 | EXPECT_EQ(expected.tuple_, s3.tuple_); |
428 | EXPECT_TRUE(str.empty()); |
429 | } |
430 | |
431 | TEST(MakeIndexSequenceFromTuple, Basic) { |
432 | using folly::index_sequence; |
433 | using folly::index_sequence_for_tuple; |
434 | using OneElementTuple = std::tuple<int>; |
435 | using TwoElementTuple = std::tuple<int>; |
436 | |
437 | EXPECT_TRUE((std::is_same< |
438 | index_sequence_for_tuple<OneElementTuple>, |
439 | index_sequence<0>>::value)); |
440 | EXPECT_TRUE((std::is_same< |
441 | index_sequence_for_tuple<const OneElementTuple>, |
442 | index_sequence<0>>::value)); |
443 | |
444 | EXPECT_TRUE((std::is_same< |
445 | index_sequence_for_tuple<TwoElementTuple>, |
446 | index_sequence<0>>::value)); |
447 | EXPECT_TRUE((std::is_same< |
448 | index_sequence_for_tuple<const TwoElementTuple>, |
449 | index_sequence<0>>::value)); |
450 | } |
451 | |
452 | TEST(ApplyResult, Basic) { |
453 | { |
454 | auto f = [](auto) -> int { return {}; }; |
455 | EXPECT_TRUE((std::is_same< |
456 | folly::apply_result_t<decltype(f), std::tuple<int>>, |
457 | int>{})); |
458 | } |
459 | |
460 | { |
461 | auto f = folly::overload( |
462 | [](int) {}, |
463 | [](double) -> double { return {}; }, |
464 | [](int, int) -> int { return {}; }); |
465 | |
466 | EXPECT_TRUE((std::is_same< |
467 | folly::apply_result_t<decltype(f), std::tuple<int>>, |
468 | void>::value)); |
469 | EXPECT_TRUE((std::is_same< |
470 | folly::apply_result_t<decltype(f), std::tuple<double>>, |
471 | double>::value)); |
472 | EXPECT_TRUE((std::is_same< |
473 | folly::apply_result_t<decltype(f), std::tuple<int, int>>, |
474 | int>::value)); |
475 | } |
476 | } |
477 | |
478 | TEST(IsApplicable, Basic) { |
479 | { |
480 | auto f = [] {}; |
481 | EXPECT_TRUE((folly::is_applicable<decltype(f), std::tuple<>>::value)); |
482 | EXPECT_FALSE((folly::is_applicable<decltype(f), std::tuple<int>>::value)); |
483 | } |
484 | { |
485 | auto f = folly::overload([](int) {}, [](double) -> double { return {}; }); |
486 | EXPECT_TRUE((folly::is_applicable<decltype(f), std::tuple<double>>::value)); |
487 | EXPECT_TRUE((folly::is_applicable<decltype(f), std::tuple<int>>::value)); |
488 | EXPECT_FALSE((folly::is_applicable<decltype(f), std::tuple<>>::value)); |
489 | EXPECT_FALSE( |
490 | (folly::is_applicable<decltype(f), std::tuple<int, double>>::value)); |
491 | } |
492 | } |
493 | |
494 | TEST(IsNothrowApplicable, Basic) { |
495 | { |
496 | auto f = []() noexcept {}; |
497 | EXPECT_TRUE((folly::is_nothrow_applicable<decltype(f), std::tuple<>>{})); |
498 | EXPECT_FALSE( |
499 | (folly::is_nothrow_applicable<decltype(f), std::tuple<int>>{})); |
500 | } |
501 | { |
502 | auto f = folly::overload( |
503 | [](int) noexcept {}, [](double) -> double { return {}; }); |
504 | EXPECT_FALSE( |
505 | (folly::is_nothrow_applicable<decltype(f), std::tuple<double>>{})); |
506 | EXPECT_TRUE((folly::is_nothrow_applicable<decltype(f), std::tuple<int>>{})); |
507 | EXPECT_FALSE((folly::is_nothrow_applicable<decltype(f), std::tuple<>>{})); |
508 | EXPECT_FALSE( |
509 | (folly::is_nothrow_applicable<decltype(f), std::tuple<int, double>>:: |
510 | value)); |
511 | } |
512 | } |
513 | |
514 | TEST(IsApplicableR, Basic) { |
515 | { |
516 | auto f = []() -> int { return {}; }; |
517 | EXPECT_TRUE((folly::is_applicable_r<double, decltype(f), std::tuple<>>{})); |
518 | EXPECT_FALSE( |
519 | (folly::is_applicable_r<double, decltype(f), std::tuple<int>>{})); |
520 | } |
521 | { |
522 | auto f = folly::overload( |
523 | [](int) noexcept {}, [](double) -> double { return {}; }); |
524 | EXPECT_TRUE( |
525 | (folly::is_applicable_r<float, decltype(f), std::tuple<double>>{})); |
526 | EXPECT_TRUE((folly::is_applicable_r<void, decltype(f), std::tuple<int>>{})); |
527 | EXPECT_FALSE((folly::is_applicable_r<void, decltype(f), std::tuple<>>{})); |
528 | EXPECT_FALSE( |
529 | (folly::is_applicable_r<double, decltype(f), std::tuple<int, double>>:: |
530 | value)); |
531 | } |
532 | } |
533 | |
534 | TEST(IsNothrowApplicableR, Basic) { |
535 | { |
536 | auto f = []() noexcept->int { |
537 | return {}; |
538 | }; |
539 | EXPECT_TRUE( |
540 | (folly::is_nothrow_applicable_r<double, decltype(f), std::tuple<>>{})); |
541 | EXPECT_FALSE( |
542 | (folly:: |
543 | is_nothrow_applicable_r<double, decltype(f), std::tuple<int>>{})); |
544 | } |
545 | { |
546 | auto f = folly::overload( |
547 | [](int) noexcept {}, [](double) -> double { return {}; }); |
548 | EXPECT_FALSE(( |
549 | folly:: |
550 | is_nothrow_applicable_r<float, decltype(f), std::tuple<double>>{})); |
551 | EXPECT_TRUE( |
552 | (folly::is_nothrow_applicable_r<void, decltype(f), std::tuple<int>>{})); |
553 | EXPECT_FALSE( |
554 | (folly::is_nothrow_applicable_r<void, decltype(f), std::tuple<>>{})); |
555 | EXPECT_FALSE((folly::is_nothrow_applicable_r< |
556 | double, |
557 | decltype(f), |
558 | std::tuple<int, double>>::value)); |
559 | } |
560 | } |
561 | |
562 | TEST(ForwardTuple, Basic) { |
563 | auto tuple = std::make_tuple(1, 2.0); |
564 | |
565 | EXPECT_TRUE((std::is_same< |
566 | decltype(folly::forward_tuple(tuple)), |
567 | std::tuple<int&, double&>>::value)); |
568 | EXPECT_EQ(folly::forward_tuple(tuple), tuple); |
569 | EXPECT_TRUE((std::is_same< |
570 | decltype(folly::forward_tuple(folly::as_const(tuple))), |
571 | std::tuple<const int&, const double&>>::value)); |
572 | EXPECT_EQ(folly::forward_tuple(folly::as_const(tuple)), tuple); |
573 | |
574 | EXPECT_TRUE((std::is_same< |
575 | decltype(folly::forward_tuple(std::move(tuple))), |
576 | std::tuple<int&&, double&&>>::value)); |
577 | EXPECT_EQ(folly::forward_tuple(std::move(tuple)), tuple); |
578 | EXPECT_TRUE( |
579 | (std::is_same< |
580 | decltype(folly::forward_tuple(std::move(folly::as_const(tuple)))), |
581 | std::tuple<const int&, const double&>>::value)); |
582 | EXPECT_EQ(folly::forward_tuple(std::move(folly::as_const(tuple))), tuple); |
583 | |
584 | auto integer = 1; |
585 | auto floating_point = 2.0; |
586 | auto ref_tuple = std::forward_as_tuple(integer, std::move(floating_point)); |
587 | |
588 | EXPECT_TRUE((std::is_same< |
589 | decltype(folly::forward_tuple(ref_tuple)), |
590 | std::tuple<int&, double&>>::value)); |
591 | |
592 | EXPECT_TRUE((std::is_same< |
593 | decltype(folly::forward_tuple(std::move(ref_tuple))), |
594 | std::tuple<int&, double&&>>::value)); |
595 | |
596 | EXPECT_TRUE((std::is_same< |
597 | decltype(std::tuple_cat( |
598 | folly::forward_tuple(tuple), |
599 | folly::forward_tuple(std::move(tuple)))), |
600 | std::tuple<int&, double&, int&&, double&&>>::value)); |
601 | } |
602 | |