зеркало из https://github.com/microsoft/STL.git
`<tuple>`: Overhaul `tuple_cat()` for simplicity and less special-casing (#2833)
Co-authored-by: ArtemSarmini <16746066+ArtemSarmini@users.noreply.github.com>
This commit is contained in:
Родитель
020aad2e08
Коммит
c34f24920e
|
@ -823,15 +823,6 @@ _NODISCARD constexpr tuple<_Types&&...> forward_as_tuple(_Types&&... _Args) noex
|
|||
return tuple<_Types&&...>(_STD forward<_Types>(_Args)...);
|
||||
}
|
||||
|
||||
template <class _Seq_type1, class _Seq_type2>
|
||||
struct _Cat_sequences;
|
||||
|
||||
template <size_t... _Indexes1, size_t... _Indexes2>
|
||||
struct _Cat_sequences<index_sequence<_Indexes1...>,
|
||||
index_sequence<_Indexes2...>> { // concatenates two index_sequence types
|
||||
using type = index_sequence<_Indexes1..., _Indexes2...>;
|
||||
};
|
||||
|
||||
template <class _Ty, size_t _Size>
|
||||
class array;
|
||||
|
||||
|
@ -847,65 +838,38 @@ _NODISCARD constexpr _Ty&& get(array<_Ty, _Size>&& _Arr) noexcept;
|
|||
template <size_t _Idx, class _Ty, size_t _Size>
|
||||
_NODISCARD constexpr const _Ty&& get(const array<_Ty, _Size>&& _Arr) noexcept;
|
||||
|
||||
template <class _Ty, class... _For_array>
|
||||
struct _View_as_tuple { // tuple_cat() supports only tuples, pairs, and arrays
|
||||
static_assert(_Always_false<_Ty>, "Unsupported tuple_cat arguments.");
|
||||
template <class _Ty, class _Kx_arg, class _Ix_arg, size_t _Ix_next, class... _Sequences>
|
||||
struct _Tuple_cat2;
|
||||
|
||||
template <class _Ty, size_t... _Kx, size_t... _Ix, size_t _Ix_next>
|
||||
struct _Tuple_cat2<_Ty, index_sequence<_Kx...>, index_sequence<_Ix...>, _Ix_next> {
|
||||
using _Ret = tuple<tuple_element_t<_Kx, _Remove_cvref_t<tuple_element_t<_Ix, _Ty>>>...>;
|
||||
using _Kx_seq = index_sequence<_Kx...>;
|
||||
using _Ix_seq = index_sequence<_Ix...>;
|
||||
};
|
||||
|
||||
template <class... _Types>
|
||||
struct _View_as_tuple<tuple<_Types...>> { // view a tuple as a tuple
|
||||
using type = tuple<_Types...>;
|
||||
};
|
||||
|
||||
template <class _Ty1, class _Ty2>
|
||||
struct _View_as_tuple<pair<_Ty1, _Ty2>> { // view a pair as a tuple
|
||||
using type = tuple<_Ty1, _Ty2>;
|
||||
};
|
||||
|
||||
template <class _Ty, class... _Types>
|
||||
struct _View_as_tuple<array<_Ty, 0>, _Types...> { // view an array as a tuple; ends recursion at 0
|
||||
using type = tuple<_Types...>;
|
||||
};
|
||||
|
||||
template <class _Ty, size_t _Size, class... _Types>
|
||||
struct _View_as_tuple<array<_Ty, _Size>, _Types...>
|
||||
: _View_as_tuple<array<_Ty, _Size - 1>, _Ty, _Types...> { // view an array as a tuple; counts down to 0
|
||||
};
|
||||
|
||||
template <size_t _Nx, class _Ty>
|
||||
struct _Repeat_for : integral_constant<size_t, _Nx> {}; // repeats _Nx for each _Ty in a parameter pack
|
||||
|
||||
template <class _Ret, class _Kx_arg, class _Ix_arg, size_t _Ix_next, class... _Tuples>
|
||||
struct _Tuple_cat2 { // determine tuple_cat's return type and _Kx/_Ix indices
|
||||
static_assert(sizeof...(_Tuples) == 0, "Unsupported tuple_cat arguments.");
|
||||
using type = _Ret;
|
||||
using _Kx_arg_seq = _Kx_arg;
|
||||
using _Ix_arg_seq = _Ix_arg;
|
||||
};
|
||||
|
||||
template <class... _Types1, class _Kx_arg, size_t... _Ix, size_t _Ix_next, class... _Types2, class... _Rest>
|
||||
struct _Tuple_cat2<tuple<_Types1...>, _Kx_arg, index_sequence<_Ix...>, _Ix_next, tuple<_Types2...>, _Rest...>
|
||||
: _Tuple_cat2<tuple<_Types1..., _Types2...>, typename _Cat_sequences<_Kx_arg, index_sequence_for<_Types2...>>::type,
|
||||
index_sequence<_Ix..., _Repeat_for<_Ix_next, _Types2>::value...>, _Ix_next + 1,
|
||||
_Rest...> { // determine tuple_cat's return type and _Kx/_Ix indices
|
||||
};
|
||||
template <class _Ty, size_t... _Kx, size_t... _Ix, size_t _Ix_next, size_t... _Kx_next, class... _Rest>
|
||||
struct _Tuple_cat2<_Ty, index_sequence<_Kx...>, index_sequence<_Ix...>, _Ix_next, index_sequence<_Kx_next...>, _Rest...>
|
||||
: _Tuple_cat2<_Ty, index_sequence<_Kx..., _Kx_next...>,
|
||||
index_sequence<_Ix..., (_Ix_next + 0 * _Kx_next)...>, // repeat _Ix_next, ignoring the elements of _Kx_next
|
||||
_Ix_next + 1, _Rest...> {};
|
||||
|
||||
template <class... _Tuples>
|
||||
struct _Tuple_cat1 : _Tuple_cat2<tuple<>, index_sequence<>, index_sequence<>, 0,
|
||||
typename _View_as_tuple<decay_t<_Tuples>>::type...> { // prepare to determine tuple_cat's
|
||||
// return type and _Kx/_Ix indices
|
||||
};
|
||||
using _Tuple_cat1 = _Tuple_cat2<tuple<_Tuples&&...>, index_sequence<>, index_sequence<>, 0,
|
||||
make_index_sequence<tuple_size_v<_Remove_cvref_t<_Tuples>>>...>;
|
||||
|
||||
template <class _Ret, size_t... _Kx, size_t... _Ix, class _Ty>
|
||||
constexpr _Ret _Tuple_cat(index_sequence<_Kx...>, index_sequence<_Ix...>, _Ty&& _Arg) { // concatenate tuples
|
||||
return _Ret(_STD get<_Kx>(_STD get<_Ix>(_STD forward<_Ty>(_Arg)))...);
|
||||
constexpr _Ret _Tuple_cat(index_sequence<_Kx...>, index_sequence<_Ix...>, _Ty _Arg) {
|
||||
return _Ret{_STD get<_Kx>(_STD get<_Ix>(_STD move(_Arg)))...};
|
||||
}
|
||||
|
||||
template <class... _Tuples>
|
||||
_NODISCARD constexpr typename _Tuple_cat1<_Tuples...>::type tuple_cat(_Tuples&&... _Tpls) { // concatenate tuples
|
||||
using _Cat1 = _Tuple_cat1<_Tuples...>;
|
||||
return _Tuple_cat<typename _Cat1::type>(typename _Cat1::_Kx_arg_seq{}, typename _Cat1::_Ix_arg_seq{},
|
||||
_STD forward_as_tuple(_STD forward<_Tuples>(_Tpls)...));
|
||||
_NODISCARD constexpr typename _Tuple_cat1<_Tuples...>::_Ret tuple_cat(_Tuples&&... _Tpls) { // concatenate tuples
|
||||
using _Cat1 = _Tuple_cat1<_Tuples...>;
|
||||
using _Ret = typename _Cat1::_Ret;
|
||||
using _Kx_seq = typename _Cat1::_Kx_seq;
|
||||
using _Ix_seq = typename _Cat1::_Ix_seq;
|
||||
return _Tuple_cat<_Ret>(_Kx_seq{}, _Ix_seq{}, _STD forward_as_tuple(_STD forward<_Tuples>(_Tpls)...));
|
||||
}
|
||||
|
||||
#if _HAS_CXX17
|
||||
|
|
|
@ -10,10 +10,52 @@
|
|||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#ifdef __cpp_lib_ranges
|
||||
#include <iterator>
|
||||
#include <list>
|
||||
#include <ranges>
|
||||
#include <vector>
|
||||
#endif // __cpp_lib_ranges
|
||||
|
||||
using namespace std;
|
||||
|
||||
#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__)
|
||||
|
||||
template <template <typename...> class PairOrTuple>
|
||||
void test_value_categories() {
|
||||
int n1 = 10;
|
||||
const int n2 = 20;
|
||||
int n3 = 30;
|
||||
const int n4 = 40;
|
||||
|
||||
PairOrTuple<char, long> obj('x', 50);
|
||||
PairOrTuple<int&, const int&> lv(n1, n2);
|
||||
PairOrTuple<int&&, const int&&> rv(move(n3), move(n4));
|
||||
|
||||
// For rvalue reference elements, we must pass the tuple-like containers as rvalues,
|
||||
// otherwise the concatenated tuple can't be constructed.
|
||||
{
|
||||
auto cat1 = tuple_cat(obj, lv);
|
||||
STATIC_ASSERT(is_same_v<decltype(cat1), tuple<char, long, int&, const int&>>);
|
||||
assert(cat1 == make_tuple('x', 50, 10, 20));
|
||||
}
|
||||
{
|
||||
auto cat2 = tuple_cat(as_const(obj), as_const(lv));
|
||||
STATIC_ASSERT(is_same_v<decltype(cat2), tuple<char, long, int&, const int&>>);
|
||||
assert(cat2 == make_tuple('x', 50, 10, 20));
|
||||
}
|
||||
{
|
||||
auto cat3 = tuple_cat(move(obj), move(lv), move(rv));
|
||||
STATIC_ASSERT(is_same_v<decltype(cat3), tuple<char, long, int&, const int&, int&&, const int&&>>);
|
||||
assert(cat3 == make_tuple('x', 50, 10, 20, 30, 40));
|
||||
}
|
||||
{
|
||||
auto cat4 = tuple_cat(move(as_const(obj)), move(as_const(lv)), move(as_const(rv)));
|
||||
STATIC_ASSERT(is_same_v<decltype(cat4), tuple<char, long, int&, const int&, int&&, const int&&>>);
|
||||
assert(cat4 == make_tuple('x', 50, 10, 20, 30, 40));
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
{
|
||||
tuple<> x;
|
||||
|
@ -169,6 +211,49 @@ int main() {
|
|||
assert(t == make_tuple(11, 22, 33U, s, 40, 50L, 60L, 'x', 'y', 'z', 1234LL));
|
||||
}
|
||||
|
||||
test_value_categories<pair>();
|
||||
test_value_categories<tuple>();
|
||||
|
||||
{
|
||||
array<int, 3> a1{{-1, -2, -3}};
|
||||
const array<char, 4> a2{{'C', 'A', 'T', 'S'}};
|
||||
|
||||
{
|
||||
auto cat5 = tuple_cat(a1, a2);
|
||||
STATIC_ASSERT(is_same_v<decltype(cat5), tuple<int, int, int, char, char, char, char>>);
|
||||
assert(cat5 == make_tuple(-1, -2, -3, 'C', 'A', 'T', 'S'));
|
||||
}
|
||||
|
||||
{
|
||||
auto cat6 = tuple_cat(move(a1), move(a2));
|
||||
STATIC_ASSERT(is_same_v<decltype(cat6), tuple<int, int, int, char, char, char, char>>);
|
||||
assert(cat6 == make_tuple(-1, -2, -3, 'C', 'A', 'T', 'S'));
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __cpp_lib_ranges
|
||||
{
|
||||
using ranges::subrange, ranges::subrange_kind;
|
||||
|
||||
list<int> lst = {10, 20, 30, 40, 50};
|
||||
using LstIter = list<int>::iterator;
|
||||
using LstSubrange = subrange<LstIter>; // test unsized
|
||||
STATIC_ASSERT(is_same_v<LstSubrange, subrange<LstIter, LstIter, subrange_kind::unsized>>);
|
||||
LstSubrange lst_subrange(next(lst.begin()), prev(lst.end()));
|
||||
|
||||
vector<int> vec = {60, 70, 80, 90, 100};
|
||||
using VecIter = vector<int>::iterator;
|
||||
using VecConstIter = vector<int>::const_iterator;
|
||||
using VecSubrange = subrange<VecIter, VecConstIter>; // test sized, and different iterator/sentinel types
|
||||
STATIC_ASSERT(is_same_v<VecSubrange, subrange<VecIter, VecConstIter, subrange_kind::sized>>);
|
||||
VecSubrange vec_subrange(vec.begin() + 1, vec.cend() - 1);
|
||||
|
||||
auto cat7 = tuple_cat(lst_subrange, vec_subrange);
|
||||
STATIC_ASSERT(is_same_v<decltype(cat7), tuple<LstIter, LstIter, VecIter, VecConstIter>>);
|
||||
assert(cat7 == make_tuple(next(lst.begin()), prev(lst.end()), vec.begin() + 1, vec.cend() - 1));
|
||||
}
|
||||
#endif // __cpp_lib_ranges
|
||||
|
||||
// Also test C++17 apply() and make_from_tuple().
|
||||
#if _HAS_CXX17
|
||||
{
|
||||
|
|
Загрузка…
Ссылка в новой задаче