`<tuple>`, `<utility>`: Make `tuple`-related functions ADL-proof (#4488)

This commit is contained in:
A. Jiang 2024-03-20 06:57:56 +08:00 коммит произвёл GitHub
Родитель 2175098dee
Коммит 4787efeae1
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
3 изменённых файлов: 222 добавлений и 105 удалений

Просмотреть файл

@ -1053,7 +1053,7 @@ _NODISCARD constexpr typename _Tuple_cat1<_Tuples...>::_Ret tuple_cat(_Tuples&&.
using _Ret = typename _Cat1::_Ret; using _Ret = typename _Cat1::_Ret;
using _Kx_seq = typename _Cat1::_Kx_seq; using _Kx_seq = typename _Cat1::_Kx_seq;
using _Ix_seq = typename _Cat1::_Ix_seq; using _Ix_seq = typename _Cat1::_Ix_seq;
return _Tuple_cat<_Ret>(_Kx_seq{}, _Ix_seq{}, _STD forward_as_tuple(_STD forward<_Tuples>(_Tpls)...)); return _STD _Tuple_cat<_Ret>(_Kx_seq{}, _Ix_seq{}, _STD forward_as_tuple(_STD forward<_Tuples>(_Tpls)...));
} }
#if _HAS_CXX17 #if _HAS_CXX17
@ -1073,9 +1073,9 @@ _EXPORT_STD template <class _Callable, _Tuple_like _Tuple>
_EXPORT_STD template <class _Callable, class _Tuple> _EXPORT_STD template <class _Callable, class _Tuple>
#endif // ^^^ !_HAS_CXX23 ^^^ #endif // ^^^ !_HAS_CXX23 ^^^
constexpr decltype(auto) apply(_Callable&& _Obj, _Tuple&& _Tpl) noexcept( constexpr decltype(auto) apply(_Callable&& _Obj, _Tuple&& _Tpl) noexcept(
noexcept(_Apply_impl(_STD forward<_Callable>(_Obj), _STD forward<_Tuple>(_Tpl), noexcept(_STD _Apply_impl(_STD forward<_Callable>(_Obj), _STD forward<_Tuple>(_Tpl),
make_index_sequence<tuple_size_v<remove_reference_t<_Tuple>>>{}))) { make_index_sequence<tuple_size_v<remove_reference_t<_Tuple>>>{}))) {
return _Apply_impl(_STD forward<_Callable>(_Obj), _STD forward<_Tuple>(_Tpl), return _STD _Apply_impl(_STD forward<_Callable>(_Obj), _STD forward<_Tuple>(_Tpl),
make_index_sequence<tuple_size_v<remove_reference_t<_Tuple>>>{}); make_index_sequence<tuple_size_v<remove_reference_t<_Tuple>>>{});
} }
@ -1097,10 +1097,10 @@ _EXPORT_STD template <class _Ty, _Tuple_like _Tuple>
#else // ^^^ _HAS_CXX23 / !_HAS_CXX23 vvv #else // ^^^ _HAS_CXX23 / !_HAS_CXX23 vvv
_EXPORT_STD template <class _Ty, class _Tuple> _EXPORT_STD template <class _Ty, class _Tuple>
#endif // ^^^ !_HAS_CXX23 ^^^ #endif // ^^^ !_HAS_CXX23 ^^^
_NODISCARD constexpr _Ty make_from_tuple(_Tuple&& _Tpl) noexcept(noexcept(_Make_from_tuple_impl<_Ty>( _NODISCARD constexpr _Ty make_from_tuple(_Tuple&& _Tpl) noexcept(noexcept(_STD _Make_from_tuple_impl<_Ty>(
_STD forward<_Tuple>(_Tpl), make_index_sequence<tuple_size_v<remove_reference_t<_Tuple>>>{}))) /* strengthened */ { _STD forward<_Tuple>(_Tpl), make_index_sequence<tuple_size_v<remove_reference_t<_Tuple>>>{}))) /* strengthened */ {
// construct _Ty from the elements of _Tpl // construct _Ty from the elements of _Tpl
return _Make_from_tuple_impl<_Ty>( return _STD _Make_from_tuple_impl<_Ty>(
_STD forward<_Tuple>(_Tpl), make_index_sequence<tuple_size_v<remove_reference_t<_Tuple>>>{}); _STD forward<_Tuple>(_Tpl), make_index_sequence<tuple_size_v<remove_reference_t<_Tuple>>>{});
} }
#endif // _HAS_CXX17 #endif // _HAS_CXX17

Просмотреть файл

@ -304,7 +304,8 @@ struct pair { // store a pair of values
template <class _Tuple1, class _Tuple2, size_t... _Indices1, size_t... _Indices2> template <class _Tuple1, class _Tuple2, size_t... _Indices1, size_t... _Indices2>
constexpr pair(_Tuple1& _Val1, _Tuple2& _Val2, index_sequence<_Indices1...>, index_sequence<_Indices2...>) constexpr pair(_Tuple1& _Val1, _Tuple2& _Val2, index_sequence<_Indices1...>, index_sequence<_Indices2...>)
: first(_Tuple_get<_Indices1>(_STD move(_Val1))...), second(_Tuple_get<_Indices2>(_STD move(_Val2))...) {} : first(_STD _Tuple_get<_Indices1>(_STD move(_Val1))...),
second(_STD _Tuple_get<_Indices2>(_STD move(_Val2))...) {}
template <class... _Types1, class... _Types2> template <class... _Types1, class... _Types2>
_CONSTEXPR20 pair(piecewise_construct_t, tuple<_Types1...> _Val1, tuple<_Types2...> _Val2) _CONSTEXPR20 pair(piecewise_construct_t, tuple<_Types1...> _Val1, tuple<_Types2...> _Val2)

Просмотреть файл

@ -3,6 +3,7 @@
#include <array> #include <array>
#include <cassert> #include <cassert>
#include <cstddef>
#include <functional> #include <functional>
#include <memory> #include <memory>
#include <string> #include <string>
@ -10,6 +11,10 @@
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
#if _HAS_CXX17
#include <string_view>
#endif // _HAS_CXX17
#if _HAS_CXX20 #if _HAS_CXX20
#include <iterator> #include <iterator>
#include <list> #include <list>
@ -21,8 +26,26 @@ using namespace std;
#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__) #define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__)
#if _HAS_CXX17
#define CONSTEXPR17 constexpr
#else // ^^^ _HAS_CXX17 / !_HAS_CXX17 vvv
#define CONSTEXPR17 inline
#endif // ^^^ !_HAS_CXX17 ^^^
#if _HAS_CXX20
#define CONSTEXPR20 constexpr
#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv
#define CONSTEXPR20 inline
#endif // ^^^ !_HAS_CXX20 ^^^
#if _HAS_CXX23
#define CONSTEXPR23 constexpr
#else // ^^^ _HAS_CXX23 / !_HAS_CXX23 vvv
#define CONSTEXPR23 inline
#endif // ^^^ !_HAS_CXX23 ^^^
template <template <typename...> class PairOrTuple> template <template <typename...> class PairOrTuple>
void test_value_categories() { constexpr void test_value_categories() {
int n1 = 10; int n1 = 10;
const int n2 = 20; const int n2 = 20;
int n3 = 30; int n3 = 30;
@ -56,7 +79,7 @@ void test_value_categories() {
} }
} }
int main() { constexpr bool test() {
{ {
tuple<> x; tuple<> x;
@ -131,6 +154,169 @@ int main() {
} }
{ {
int w = 11, x = 22, y = 33, z = 44;
tuple<int&, const int&, int&&, const int&&> t0(w, x, move(y), move(z));
assert(&get<0>(t0) == &w);
assert(&get<1>(t0) == &x);
assert(&get<2>(t0) == &y);
assert(&get<3>(t0) == &z);
auto t1 = tuple_cat(make_tuple(50, 60), move(t0), make_tuple(70, 80));
STATIC_ASSERT(is_same_v<decltype(t1), tuple<int, int, int&, const int&, int&&, const int&&, int, int>>);
assert(t1 == make_tuple(50, 60, 11, 22, 33, 44, 70, 80));
assert(&get<2>(t1) == &w);
assert(&get<3>(t1) == &x);
assert(&get<4>(t1) == &y);
assert(&get<5>(t1) == &z);
}
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'));
}
}
// Also test C++17 apply() and make_from_tuple().
#if _HAS_CXX17
{
struct Point {
int x;
int y;
constexpr int add(int z) const {
return x + y + z;
}
};
const Point p{100, 20};
assert(apply(&Point::add, make_tuple(&p, 3)) == 123);
assert(make_from_tuple<string_view>(make_tuple("Hello world!", static_cast<size_t>(5))) == "Hello");
}
#endif // _HAS_CXX17
// Also test VSO-181496 "Variadic template emits spurious warning C4100: unreferenced formal parameter".
{
assert(tuple_cat() == tuple<>{});
#if _HAS_CXX17
assert(apply([] { return 1729; }, tuple<>{}) == 1729);
assert(make_from_tuple<int>(tuple<>{}) == 0);
#endif // _HAS_CXX17
}
return true;
}
#ifndef _M_CEE // TRANSITION, VSO-1659496
// Also test GH-140 "STL: We should _STD qualify _Ugly function calls to avoid ADL"
template <class T>
struct holder {
T t;
};
struct incomplete;
using validator = holder<incomplete>*;
constexpr bool test_adl_proof() {
// Function calls are intentionally qualified.
auto concatenated = std::tuple_cat(std::make_tuple(validator{}), std::make_tuple(42));
STATIC_ASSERT(is_same_v<decltype(concatenated), tuple<validator, int>>);
assert(std::get<0>(concatenated) == nullptr);
assert(std::get<1>(concatenated) == 42);
#if _HAS_CXX17
assert(std::make_from_tuple<validator>(std::make_tuple(validator{})) == nullptr);
assert(std::apply([](auto) { return 42; }, std::make_tuple(validator{})) == 42);
#endif // _HAS_CXX17
return true;
}
#endif // ^^^ no workaround ^^^
CONSTEXPR17 bool test_using_lambda() {
pair<int, int> p(11, 22);
const char* const s = "meow";
array<short, 0> zero;
array<int, 1> one = {{40}};
array<long, 2> two = {{50L, 60L}};
auto make_array = [] {
array<char, 3> ret = {{'x', 'y', 'z'}};
return ret;
};
const auto t = tuple_cat(p, make_pair(33U, s), zero, one, two, make_array(), make_tuple(1234LL));
STATIC_ASSERT(is_same_v<decltype(t),
const tuple<int, int, unsigned int, const char*, int, long, long, char, char, char, long long>>);
assert(t == make_tuple(11, 22, 33U, s, 40, 50L, 60L, 'x', 'y', 'z', 1234LL));
return true;
}
CONSTEXPR20 bool test_bind_and_piecewise_construction() {
// Also test VSO-181496 "Variadic template emits spurious warning C4100: unreferenced formal parameter".
auto b1 = bind([] { return 256; });
assert(b1() == 256);
auto b2 = bind([](int i) { return i * 3; }, b1);
assert(b2() == 768);
const pair<int, int> p(piecewise_construct, tuple<>{}, tuple<>{});
assert(p.first == 0);
assert(p.second == 0);
#ifndef _M_CEE // TRANSITION, VSO-1659496
const pair<validator, int> p2(piecewise_construct, tuple<validator>{}, tuple<int>{});
assert(p2.first == nullptr);
assert(p2.second == 0);
#endif // ^^^ no workaround ^^^
return true;
}
#if _HAS_CXX17
// Also test C++17 make_from_tuple() and C++20 constexpr string.
CONSTEXPR20 bool test_make_from_tuple_and_string() {
assert(make_from_tuple<string>(tuple<>{}).empty());
assert(make_from_tuple<string>(make_tuple(static_cast<size_t>(5), 's')) == "sssss");
assert(make_from_tuple<wstring>(tuple<>{}).empty());
assert(make_from_tuple<wstring>(make_tuple(static_cast<size_t>(5), L's')) == L"sssss");
return true;
}
#endif // _HAS_CXX17
CONSTEXPR23 bool test_unique_ptr_and_string() {
unique_ptr<int> up; unique_ptr<int> up;
up.reset(new int(10)); up.reset(new int(10));
@ -164,75 +350,12 @@ int main() {
assert(*get<8>(t0) == 30); assert(*get<8>(t0) == 30);
assert(get<9>(t0) == 44); assert(get<9>(t0) == 44);
assert(get<10>(t0) == 55); assert(get<10>(t0) == 55);
}
{ return true;
int w = 11, x = 22, y = 33, z = 44; }
tuple<int&, const int&, int&&, const int&&> t0(w, x, move(y), move(z));
assert(&get<0>(t0) == &w);
assert(&get<1>(t0) == &x);
assert(&get<2>(t0) == &y);
assert(&get<3>(t0) == &z);
auto t1 = tuple_cat(make_tuple(50, 60), move(t0), make_tuple(70, 80));
STATIC_ASSERT(is_same_v<decltype(t1), tuple<int, int, int&, const int&, int&&, const int&&, int, int>>);
assert(t1 == make_tuple(50, 60, 11, 22, 33, 44, 70, 80));
assert(&get<2>(t1) == &w);
assert(&get<3>(t1) == &x);
assert(&get<4>(t1) == &y);
assert(&get<5>(t1) == &z);
}
{
pair<int, int> p(11, 22);
const char* const s = "meow";
array<short, 0> zero;
array<int, 1> one = {{40}};
array<long, 2> two = {{50L, 60L}};
auto make_array = [] {
array<char, 3> ret = {{'x', 'y', 'z'}};
return ret;
};
const auto t = tuple_cat(p, make_pair(33U, s), zero, one, two, make_array(), make_tuple(1234LL));
STATIC_ASSERT(is_same_v<decltype(t),
const tuple<int, int, unsigned int, const char*, int, long, long, char, char, char, long long>>);
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'));
}
}
#if _HAS_CXX20 #if _HAS_CXX20
{ void test_subrange_and_containers() {
using ranges::subrange, ranges::subrange_kind; using ranges::subrange, ranges::subrange_kind;
list<int> lst = {10, 20, 30, 40, 50}; list<int> lst = {10, 20, 30, 40, 50};
@ -251,53 +374,11 @@ int main() {
auto cat7 = tuple_cat(lst_subrange, vec_subrange); auto cat7 = tuple_cat(lst_subrange, vec_subrange);
STATIC_ASSERT(is_same_v<decltype(cat7), tuple<LstIter, LstIter, VecIter, VecConstIter>>); 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)); assert(cat7 == make_tuple(next(lst.begin()), prev(lst.end()), vec.begin() + 1, vec.cend() - 1));
} }
#endif // _HAS_CXX20 #endif // _HAS_CXX20
// Also test C++17 apply() and make_from_tuple(). // LWG-3211 std::tuple<> should be trivially constructible
#if _HAS_CXX17 STATIC_ASSERT(is_trivially_default_constructible_v<tuple<>>);
{
struct Point {
int x;
int y;
int add(int z) const {
return x + y + z;
}
};
const Point p{100, 20};
assert(apply(&Point::add, make_tuple(&p, 3)) == 123);
assert(make_from_tuple<string>(make_tuple(static_cast<size_t>(5), 's')) == "sssss");
}
#endif // _HAS_CXX17
// Also test VSO-181496 "Variadic template emits spurious warning C4100: unreferenced formal parameter".
{
auto b1 = bind([] { return 256; });
assert(b1() == 256);
auto b2 = bind([](int i) { return i * 3; }, b1);
assert(b2() == 768);
const pair<int, int> p(piecewise_construct, tuple<>{}, tuple<>{});
assert(p.first == 0);
assert(p.second == 0);
assert(tuple_cat() == tuple<>{});
#if _HAS_CXX17
assert(apply([] { return 1729; }, tuple<>{}) == 1729);
assert(make_from_tuple<int>(tuple<>{}) == 0);
#endif // _HAS_CXX17
}
// LWG-3211 std::tuple<> should be trivially constructible
STATIC_ASSERT(is_trivially_default_constructible_v<tuple<>>);
}
// Also test DevDiv-1205400 "C++ compiler: static_assert in std::tuple_element prevents SFINAE". // Also test DevDiv-1205400 "C++ compiler: static_assert in std::tuple_element prevents SFINAE".
template <typename T, typename = void> template <typename T, typename = void>
@ -318,3 +399,38 @@ struct HasTupleSize<T, void_t<typename tuple_size<T>::type>> : true_type {};
STATIC_ASSERT(!HasTupleSize<int>::value); STATIC_ASSERT(!HasTupleSize<int>::value);
STATIC_ASSERT(HasTupleSize<tuple<short, long>>::value); STATIC_ASSERT(HasTupleSize<tuple<short, long>>::value);
STATIC_ASSERT(test());
#ifndef _M_CEE // TRANSITION, VSO-1659496
STATIC_ASSERT(test_adl_proof());
#endif // ^^^ no workaround ^^^
#if _HAS_CXX17
STATIC_ASSERT(test_using_lambda());
#endif // _HAS_CXX17
#if _HAS_CXX20
STATIC_ASSERT(test_bind_and_piecewise_construction());
STATIC_ASSERT(test_make_from_tuple_and_string());
#endif // _HAS_CXX20
#if _HAS_CXX23
STATIC_ASSERT(test_unique_ptr_and_string());
#endif // _HAS_CXX23
int main() {
test();
#ifndef _M_CEE // TRANSITION, VSO-1659496
test_adl_proof();
#endif // ^^^ no workaround ^^^
test_using_lambda();
test_bind_and_piecewise_construction();
#if _HAS_CXX17
test_make_from_tuple_and_string();
#endif // _HAS_CXX17
#if _HAS_CXX20
test_subrange_and_containers();
#endif //_HAS_CXX20
test_unique_ptr_and_string();
}