1 | //===----------------------------------------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 |
10 | |
11 | // template <class O, class T> |
12 | // struct out_value_result; |
13 | |
14 | #include <algorithm> |
15 | #include <cassert> |
16 | #include <type_traits> |
17 | |
18 | #include "MoveOnly.h" |
19 | |
20 | using std::ranges::out_value_result; |
21 | |
22 | // |
23 | // Helper structs |
24 | // |
25 | |
26 | // only explicit construction |
27 | struct IterTypeExplicit { |
28 | explicit IterTypeExplicit(int*); |
29 | }; |
30 | |
31 | // implicit construction |
32 | struct IterTypeImplicit { |
33 | IterTypeImplicit(int*); |
34 | }; |
35 | |
36 | struct IterTypeImplicitRef { |
37 | IterTypeImplicitRef(int&); |
38 | }; |
39 | |
40 | struct NotConvertible {}; |
41 | |
42 | template <class T> |
43 | struct ConvertibleFrom { |
44 | constexpr ConvertibleFrom(T c) : content{c} {} |
45 | T content; |
46 | }; |
47 | |
48 | // Standard layout classes can't have virtual functions |
49 | struct NonStandardLayoutTypeBase { |
50 | virtual ~NonStandardLayoutTypeBase(); |
51 | }; |
52 | struct NonStandardLayoutType : public NonStandardLayoutTypeBase {}; |
53 | |
54 | // |
55 | constexpr bool test_constraints() { |
56 | // requires convertible_to<const _OutIter1&, _OutIter2> && convertible_to<const _ValType1&, _ValType2> |
57 | static_assert(std::is_constructible_v<out_value_result<int*, int>, out_value_result<int*, int>>); |
58 | |
59 | // test failure when implicit conversion isn't allowed |
60 | static_assert(!std::is_constructible_v<out_value_result<IterTypeExplicit, int>, out_value_result<int*, int>>); |
61 | |
62 | // test success when implicit conversion is allowed, checking combinations of value, reference, and const |
63 | static_assert(std::is_constructible_v<out_value_result<IterTypeImplicit, int>, out_value_result<int*, int>>); |
64 | static_assert(std::is_constructible_v<out_value_result<IterTypeImplicit, int>, out_value_result<int*, int> const>); |
65 | static_assert(std::is_constructible_v<out_value_result<IterTypeImplicit, int>, out_value_result<int*, int>&>); |
66 | static_assert(std::is_constructible_v<out_value_result<IterTypeImplicit, int>, out_value_result<int*, int> const&>); |
67 | |
68 | static_assert(!std::is_constructible_v<out_value_result<IterTypeImplicitRef, int>, out_value_result<int, int>&>); |
69 | |
70 | // has to be convertible via const& |
71 | static_assert(std::is_convertible_v<out_value_result<int, int>&, out_value_result<long, long>>); |
72 | static_assert(std::is_convertible_v<const out_value_result<int, int>&, out_value_result<long, long>>); |
73 | static_assert(std::is_convertible_v<out_value_result<int, int>&&, out_value_result<long, long>>); |
74 | static_assert(std::is_convertible_v<const out_value_result<int, int>&&, out_value_result<long, long>>); |
75 | |
76 | // should be move constructible |
77 | static_assert(std::is_move_constructible_v<out_value_result<MoveOnly, int>>); |
78 | static_assert(std::is_move_constructible_v<out_value_result<int, MoveOnly>>); |
79 | |
80 | // conversions should not work if there is no conversion |
81 | static_assert(!std::is_convertible_v<out_value_result<NotConvertible, int>, out_value_result<int, NotConvertible>>); |
82 | static_assert(!std::is_convertible_v<out_value_result<int, NotConvertible>, out_value_result<NotConvertible, int>>); |
83 | |
84 | // check standard layout |
85 | static_assert(std::is_standard_layout_v<out_value_result<int, int>>); |
86 | static_assert(!std::is_standard_layout_v<out_value_result<NonStandardLayoutType, int>>); |
87 | |
88 | return true; |
89 | } |
90 | |
91 | // Test results |
92 | constexpr bool test() { |
93 | { |
94 | // Check that conversion operator works |
95 | out_value_result<double, int> res{10, 1}; |
96 | assert(res.out == 10); |
97 | assert(res.value == 1); |
98 | out_value_result<ConvertibleFrom<double>, ConvertibleFrom<int>> res2 = res; |
99 | assert(res2.out.content == 10); |
100 | assert(res2.value.content == 1); |
101 | } |
102 | { |
103 | // Check that out_value_result isn't overconstrained w.r.t. move/copy constructors |
104 | out_value_result<MoveOnly, int> res{MoveOnly{}, 10}; |
105 | assert(res.out.get() == 1); |
106 | assert(res.value == 10); |
107 | auto res2 = std::move(res); |
108 | assert(res.out.get() == 0); |
109 | assert(res.value == 10); |
110 | assert(res2.out.get() == 1); |
111 | assert(res2.value == 10); |
112 | } |
113 | { |
114 | // Check structured binding |
115 | auto [out, val] = out_value_result<int, int>{1, 2}; |
116 | assert(out == 1); |
117 | assert(val == 2); |
118 | } |
119 | { |
120 | // Check default construction |
121 | out_value_result<int, double> res; |
122 | static_assert(std::is_same_v<int, decltype(res.out)>); |
123 | static_assert(std::is_same_v<double, decltype(res.value)>); |
124 | } |
125 | { |
126 | // Check aggregate initiazliation |
127 | out_value_result<int, int> res = {1, 2}; |
128 | assert(res.out == 1); |
129 | assert(res.value == 2); |
130 | } |
131 | |
132 | return true; |
133 | } |
134 | |
135 | int main(int, char**) { |
136 | test_constraints(); |
137 | static_assert(test_constraints()); |
138 | test(); |
139 | static_assert(test()); |
140 | return 0; |
141 | } |
142 | |