Add is_swappable/is_nothrow_swappable traits
llvm-svn: 267079
This commit is contained in:
parent
c89755e4cb
commit
f07dd8d0a9
|
@ -630,7 +630,7 @@ template <class BidirectionalIterator, class Compare>
|
|||
#include <initializer_list>
|
||||
#include <type_traits>
|
||||
#include <cstring>
|
||||
#include <utility>
|
||||
#include <utility> // needed to provide swap_ranges.
|
||||
#include <memory>
|
||||
#include <iterator>
|
||||
#include <cstddef>
|
||||
|
|
|
@ -34,7 +34,7 @@ struct array
|
|||
|
||||
// No explicit construct/copy/destroy for aggregate type
|
||||
void fill(const T& u);
|
||||
void swap(array& a) noexcept(noexcept(swap(declval<T&>(), declval<T&>())));
|
||||
void swap(array& a) noexcept(is_nothrow_swappable_v<T>);
|
||||
|
||||
// iterators:
|
||||
iterator begin() noexcept;
|
||||
|
@ -141,8 +141,15 @@ struct _LIBCPP_TYPE_VIS_ONLY array
|
|||
_LIBCPP_INLINE_VISIBILITY void fill(const value_type& __u)
|
||||
{_VSTD::fill_n(__elems_, _Size, __u);}
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
void swap(array& __a) _NOEXCEPT_(__is_nothrow_swappable<_Tp>::value)
|
||||
{_VSTD::swap_ranges(__elems_, __elems_ + _Size, __a.__elems_);}
|
||||
void swap(array& __a) _NOEXCEPT_(_Size == 0 || __is_nothrow_swappable<_Tp>::value)
|
||||
{ __swap_dispatch((std::integral_constant<bool, _Size == 0>()), __a); }
|
||||
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
void __swap_dispatch(std::true_type, array&) {}
|
||||
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
void __swap_dispatch(std::false_type, array& __a)
|
||||
{ _VSTD::swap_ranges(__elems_, __elems_ + _Size, __a.__elems_);}
|
||||
|
||||
// iterators:
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
|
@ -276,11 +283,12 @@ template <class _Tp, size_t _Size>
|
|||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
typename enable_if
|
||||
<
|
||||
_Size == 0 ||
|
||||
__is_swappable<_Tp>::value,
|
||||
void
|
||||
>::type
|
||||
swap(array<_Tp, _Size>& __x, array<_Tp, _Size>& __y)
|
||||
_NOEXCEPT_(__is_nothrow_swappable<_Tp>::value)
|
||||
_NOEXCEPT_(noexcept(__x.swap(__y)))
|
||||
{
|
||||
__x.swap(__y);
|
||||
}
|
||||
|
|
|
@ -162,7 +162,7 @@ public:
|
|||
|
||||
void swap(map& m)
|
||||
noexcept(allocator_traits<allocator_type>::is_always_equal::value &&
|
||||
__is_nothrow_swappable<key_compare>::value); // C++17
|
||||
is_nothrow_swappable<key_compare>::value); // C++17
|
||||
|
||||
// observers:
|
||||
allocator_type get_allocator() const noexcept;
|
||||
|
@ -357,7 +357,7 @@ public:
|
|||
|
||||
void swap(multimap& m)
|
||||
noexcept(allocator_traits<allocator_type>::is_always_equal::value &&
|
||||
__is_nothrow_swappable<key_compare>::value); // C++17
|
||||
is_nothrow_swappable<key_compare>::value); // C++17
|
||||
|
||||
// observers:
|
||||
allocator_type get_allocator() const noexcept;
|
||||
|
|
|
@ -2974,7 +2974,10 @@ private:
|
|||
|
||||
template <class _Tp, class _Dp>
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
void
|
||||
typename enable_if<
|
||||
__is_swappable<_Dp>::value,
|
||||
void
|
||||
>::type
|
||||
swap(unique_ptr<_Tp, _Dp>& __x, unique_ptr<_Tp, _Dp>& __y) _NOEXCEPT {__x.swap(__y);}
|
||||
|
||||
template <class _T1, class _D1, class _T2, class _D2>
|
||||
|
|
|
@ -66,7 +66,7 @@ public:
|
|||
template <class... Args> void emplace(Args&&... args);
|
||||
void pop();
|
||||
|
||||
void swap(queue& q) noexcept(noexcept(swap(c, q.c)));
|
||||
void swap(queue& q) noexcept(is_nothrow_swappable_v<Container>)
|
||||
};
|
||||
|
||||
template <class T, class Container>
|
||||
|
@ -153,7 +153,8 @@ public:
|
|||
void pop();
|
||||
|
||||
void swap(priority_queue& q)
|
||||
noexcept(noexcept(swap(c, q.c)) && noexcept(swap(comp.q.comp)));
|
||||
noexcept(is_nothrow_swappable_v<Container> &&
|
||||
is_nothrow_swappable_v<Comp>)
|
||||
};
|
||||
|
||||
template <class T, class Container, class Compare>
|
||||
|
@ -369,7 +370,10 @@ operator<=(const queue<_Tp, _Container>& __x,const queue<_Tp, _Container>& __y)
|
|||
|
||||
template <class _Tp, class _Container>
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
void
|
||||
typename enable_if<
|
||||
__is_swappable<_Container>::value,
|
||||
void
|
||||
>::type
|
||||
swap(queue<_Tp, _Container>& __x, queue<_Tp, _Container>& __y)
|
||||
_NOEXCEPT_(_NOEXCEPT_(__x.swap(__y)))
|
||||
{
|
||||
|
@ -700,7 +704,11 @@ priority_queue<_Tp, _Container, _Compare>::swap(priority_queue& __q)
|
|||
|
||||
template <class _Tp, class _Container, class _Compare>
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
void
|
||||
typename enable_if<
|
||||
__is_swappable<_Container>::value
|
||||
&& __is_swappable<_Compare>::value,
|
||||
void
|
||||
>::type
|
||||
swap(priority_queue<_Tp, _Container, _Compare>& __x,
|
||||
priority_queue<_Tp, _Container, _Compare>& __y)
|
||||
_NOEXCEPT_(_NOEXCEPT_(__x.swap(__y)))
|
||||
|
|
|
@ -58,7 +58,7 @@ public:
|
|||
template <class... Args> void emplace(Args&&... args);
|
||||
void pop();
|
||||
|
||||
void swap(stack& c) noexcept(noexcept(swap(c, q.c)));
|
||||
void swap(stack& c) noexcept(is_nothrow_swappable_v<Container>)
|
||||
};
|
||||
|
||||
template <class T, class Container>
|
||||
|
@ -275,7 +275,10 @@ operator<=(const stack<_Tp, _Container>& __x, const stack<_Tp, _Container>& __y)
|
|||
|
||||
template <class _Tp, class _Container>
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
void
|
||||
typename enable_if<
|
||||
__is_swappable<_Container>::value,
|
||||
void
|
||||
>::type
|
||||
swap(stack<_Tp, _Container>& __x, stack<_Tp, _Container>& __y)
|
||||
_NOEXCEPT_(_NOEXCEPT_(__x.swap(__y)))
|
||||
{
|
||||
|
|
|
@ -105,6 +105,8 @@ namespace std
|
|||
template <class T, class U> struct is_assignable;
|
||||
template <class T> struct is_copy_assignable;
|
||||
template <class T> struct is_move_assignable;
|
||||
template <class T, class U> struct is_swappable_with; // C++17
|
||||
template <class T> struct is_swappable; // C++17
|
||||
template <class T> struct is_destructible;
|
||||
|
||||
template <class T, class... Args> struct is_trivially_constructible;
|
||||
|
@ -123,6 +125,8 @@ namespace std
|
|||
template <class T, class U> struct is_nothrow_assignable;
|
||||
template <class T> struct is_nothrow_copy_assignable;
|
||||
template <class T> struct is_nothrow_move_assignable;
|
||||
template <class T, class U> struct is_nothrow_swappable_with; // C++17
|
||||
template <class T> struct is_nothrow_swappable; // C++17
|
||||
template <class T> struct is_nothrow_destructible;
|
||||
|
||||
template <class T> struct has_virtual_destructor;
|
||||
|
@ -300,6 +304,10 @@ namespace std
|
|||
= is_copy_assignable<T>::value; // C++17
|
||||
template <class T> constexpr bool is_move_assignable_v
|
||||
= is_move_assignable<T>::value; // C++17
|
||||
template <class T, class U> constexpr bool is_swappable_with_v
|
||||
= is_swappable_with<T, U>::value; // C++17
|
||||
template <class T> constexpr bool is_swappable_v
|
||||
= is_swappable<T>::value; // C++17
|
||||
template <class T> constexpr bool is_destructible_v
|
||||
= is_destructible<T>::value; // C++17
|
||||
template <class T, class... Args> constexpr bool is_trivially_constructible_v
|
||||
|
@ -332,6 +340,10 @@ namespace std
|
|||
= is_nothrow_copy_assignable<T>::value; // C++17
|
||||
template <class T> constexpr bool is_nothrow_move_assignable_v
|
||||
= is_nothrow_move_assignable<T>::value; // C++17
|
||||
template <class T, class U> constexpr bool is_nothrow_swappable_with_v
|
||||
= is_nothrow_swappable_with<T, U>::value; // C++17
|
||||
template <class T> constexpr bool is_nothrow_swappable_v
|
||||
= is_nothrow_swappable<T>::value; // C++17
|
||||
template <class T> constexpr bool is_nothrow_destructible_v
|
||||
= is_nothrow_destructible<T>::value; // C++17
|
||||
template <class T> constexpr bool has_virtual_destructor_v
|
||||
|
@ -4421,6 +4433,9 @@ constexpr bool is_nothrow_callable_v = is_nothrow_callable<_Fn, _Ret>::value;
|
|||
|
||||
#endif // !defined(_LIBCPP_CXX03_LANG)
|
||||
|
||||
template <class _Tp> struct __is_swappable;
|
||||
template <class _Tp> struct __is_nothrow_swappable;
|
||||
|
||||
template <class _Tp>
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
#ifndef _LIBCPP_HAS_NO_ADVANCED_SFINAE
|
||||
|
@ -4440,6 +4455,13 @@ swap(_Tp& __x, _Tp& __y) _NOEXCEPT_(is_nothrow_move_constructible<_Tp>::value &&
|
|||
__y = _VSTD::move(__t);
|
||||
}
|
||||
|
||||
template<class _Tp, size_t _Np>
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
typename enable_if<
|
||||
__is_swappable<_Tp>::value
|
||||
>::type
|
||||
swap(_Tp (&__a)[_Np], _Tp (&__b)[_Np]) _NOEXCEPT_(__is_nothrow_swappable<_Tp>::value);
|
||||
|
||||
template <class _ForwardIterator1, class _ForwardIterator2>
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
void
|
||||
|
@ -4455,55 +4477,103 @@ iter_swap(_ForwardIterator1 __a, _ForwardIterator2 __b)
|
|||
|
||||
namespace __detail
|
||||
{
|
||||
|
||||
// ALL generic swap overloads MUST already have a declaration available at this point.
|
||||
using _VSTD::swap;
|
||||
__nat swap(__any, __any);
|
||||
|
||||
template <class _Tp>
|
||||
struct __swappable
|
||||
template <class _Tp, class _Up = _Tp,
|
||||
bool _NotVoid = !is_void<_Tp>::value && !is_void<_Up>::value>
|
||||
struct __swappable_with
|
||||
{
|
||||
typedef decltype(swap(_VSTD::declval<_Tp&>(), _VSTD::declval<_Tp&>())) type;
|
||||
static const bool value = !is_same<type, __nat>::value;
|
||||
typedef decltype(swap(_VSTD::declval<_Tp>(), _VSTD::declval<_Up>())) __swap1;
|
||||
typedef decltype(swap(_VSTD::declval<_Up>(), _VSTD::declval<_Tp>())) __swap2;
|
||||
|
||||
static const bool value = !is_same<__swap1, __nat>::value
|
||||
&& !is_same<__swap2, __nat>::value;
|
||||
};
|
||||
|
||||
template <class _Tp, class _Up>
|
||||
struct __swappable_with<_Tp, _Up, false> : false_type {};
|
||||
|
||||
template <class _Tp, class _Up = _Tp, bool _Swappable = __swappable_with<_Tp, _Up>::value>
|
||||
struct __nothrow_swappable_with {
|
||||
static const bool value =
|
||||
#ifndef _LIBCPP_HAS_NO_NOEXCEPT
|
||||
noexcept(swap(_VSTD::declval<_Tp>(), _VSTD::declval<_Up>()))
|
||||
&& noexcept(swap(_VSTD::declval<_Up>(), _VSTD::declval<_Tp>()));
|
||||
#else
|
||||
false;
|
||||
#endif
|
||||
};
|
||||
|
||||
template <class _Tp, class _Up>
|
||||
struct __nothrow_swappable_with<_Tp, _Up, false> : false_type {};
|
||||
|
||||
} // __detail
|
||||
|
||||
template <class _Tp>
|
||||
struct __is_swappable
|
||||
: public integral_constant<bool, __detail::__swappable<_Tp>::value>
|
||||
{
|
||||
};
|
||||
|
||||
#if __has_feature(cxx_noexcept) || (_GNUC_VER >= 407 && __cplusplus >= 201103L)
|
||||
|
||||
template <bool, class _Tp>
|
||||
struct __is_nothrow_swappable_imp
|
||||
: public integral_constant<bool, noexcept(swap(_VSTD::declval<_Tp&>(),
|
||||
_VSTD::declval<_Tp&>()))>
|
||||
{
|
||||
};
|
||||
|
||||
template <class _Tp>
|
||||
struct __is_nothrow_swappable_imp<false, _Tp>
|
||||
: public false_type
|
||||
: public integral_constant<bool, __detail::__swappable_with<_Tp&>::value>
|
||||
{
|
||||
};
|
||||
|
||||
template <class _Tp>
|
||||
struct __is_nothrow_swappable
|
||||
: public __is_nothrow_swappable_imp<__is_swappable<_Tp>::value, _Tp>
|
||||
: public integral_constant<bool, __detail::__nothrow_swappable_with<_Tp&>::value>
|
||||
{
|
||||
};
|
||||
|
||||
#else // __has_feature(cxx_noexcept)
|
||||
#if _LIBCPP_STD_VER > 14
|
||||
|
||||
template <class _Tp, class _Up>
|
||||
struct _LIBCPP_TYPE_VIS_ONLY is_swappable_with
|
||||
: public integral_constant<bool, __detail::__swappable_with<_Tp, _Up>::value>
|
||||
{
|
||||
};
|
||||
|
||||
template <class _Tp>
|
||||
struct __is_nothrow_swappable
|
||||
: public false_type
|
||||
struct _LIBCPP_TYPE_VIS_ONLY is_swappable
|
||||
: public conditional<
|
||||
__is_referenceable<_Tp>::value,
|
||||
is_swappable_with<
|
||||
typename add_lvalue_reference<_Tp>::type,
|
||||
typename add_lvalue_reference<_Tp>::type>,
|
||||
false_type
|
||||
>::type
|
||||
{
|
||||
};
|
||||
|
||||
#endif // __has_feature(cxx_noexcept)
|
||||
template <class _Tp, class _Up>
|
||||
struct _LIBCPP_TYPE_VIS_ONLY is_nothrow_swappable_with
|
||||
: public integral_constant<bool, __detail::__nothrow_swappable_with<_Tp, _Up>::value>
|
||||
{
|
||||
};
|
||||
|
||||
template <class _Tp>
|
||||
struct _LIBCPP_TYPE_VIS_ONLY is_nothrow_swappable
|
||||
: public conditional<
|
||||
__is_referenceable<_Tp>::value,
|
||||
is_nothrow_swappable_with<
|
||||
typename add_lvalue_reference<_Tp>::type,
|
||||
typename add_lvalue_reference<_Tp>::type>,
|
||||
false_type
|
||||
>::type
|
||||
{
|
||||
};
|
||||
|
||||
template <class _Tp, class _Up>
|
||||
constexpr bool is_swappable_with_v = is_swappable_with<_Tp, _Up>::value;
|
||||
|
||||
template <class _Tp>
|
||||
constexpr bool is_swappable_v = is_swappable<_Tp>::value;
|
||||
|
||||
template <class _Tp, class _Up>
|
||||
constexpr bool is_nothrow_swappable_with_v = is_nothrow_swappable_with<_Tp, _Up>::value;
|
||||
|
||||
template <class _Tp>
|
||||
constexpr bool is_nothrow_swappable_v = is_nothrow_swappable<_Tp>::value;
|
||||
|
||||
#endif // _LIBCPP_STD_VER > 14
|
||||
|
||||
#ifdef _LIBCPP_UNDERLYING_TYPE
|
||||
|
||||
|
|
|
@ -82,8 +82,8 @@ struct pair
|
|||
is_nothrow_move_assignable<T2>::value);
|
||||
template <class U, class V> pair& operator=(pair<U, V>&& p);
|
||||
|
||||
void swap(pair& p) noexcept(noexcept(swap(first, p.first)) &&
|
||||
noexcept(swap(second, p.second)));
|
||||
void swap(pair& p) noexcept(is_nothrow_swappable_v<T1> &&
|
||||
is_nothrow_swappable_v<T2>);
|
||||
};
|
||||
|
||||
template <class T1, class T2> bool operator==(const pair<T1,T2>&, const pair<T1,T2>&); // constexpr in C++14
|
||||
|
@ -225,10 +225,6 @@ operator>=(const _Tp& __x, const _Tp& __y)
|
|||
|
||||
// swap_ranges
|
||||
|
||||
// forward
|
||||
template<class _Tp, size_t _Np>
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
void swap(_Tp (&__a)[_Np], _Tp (&__b)[_Np]) _NOEXCEPT_(__is_nothrow_swappable<_Tp>::value);
|
||||
|
||||
template <class _ForwardIterator1, class _ForwardIterator2>
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
|
@ -240,9 +236,12 @@ swap_ranges(_ForwardIterator1 __first1, _ForwardIterator1 __last1, _ForwardItera
|
|||
return __first2;
|
||||
}
|
||||
|
||||
// forward declared in <type_traits>
|
||||
template<class _Tp, size_t _Np>
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
void
|
||||
typename enable_if<
|
||||
__is_swappable<_Tp>::value
|
||||
>::type
|
||||
swap(_Tp (&__a)[_Np], _Tp (&__b)[_Np]) _NOEXCEPT_(__is_nothrow_swappable<_Tp>::value)
|
||||
{
|
||||
_VSTD::swap_ranges(__a, __a + _Np, __b);
|
||||
|
|
|
@ -14,10 +14,28 @@
|
|||
#include <array>
|
||||
#include <cassert>
|
||||
|
||||
#include "test_macros.h"
|
||||
// std::array is explicitly allowed to be initialized with A a = { init-list };.
|
||||
// Disable the missing braces warning for this reason.
|
||||
#include "disable_missing_braces_warning.h"
|
||||
|
||||
struct NonSwappable {
|
||||
NonSwappable() {}
|
||||
private:
|
||||
NonSwappable(NonSwappable const&);
|
||||
NonSwappable& operator=(NonSwappable const&);
|
||||
};
|
||||
|
||||
template <class Tp>
|
||||
decltype(swap(std::declval<Tp>(), std::declval<Tp>()))
|
||||
can_swap_imp(int);
|
||||
|
||||
template <class Tp>
|
||||
std::false_type can_swap_imp(...);
|
||||
|
||||
template <class Tp>
|
||||
struct can_swap : std::is_same<decltype(can_swap_imp<Tp>(0)), void> {};
|
||||
|
||||
int main()
|
||||
{
|
||||
{
|
||||
|
@ -44,4 +62,17 @@ int main()
|
|||
assert(c1.size() == 0);
|
||||
assert(c2.size() == 0);
|
||||
}
|
||||
{
|
||||
typedef NonSwappable T;
|
||||
typedef std::array<T, 42> C1;
|
||||
typedef std::array<T, 0> C0;
|
||||
static_assert(!can_swap<C1&>::value, "");
|
||||
static_assert(can_swap<C0&>::value, "");
|
||||
C0 l = {};
|
||||
C0 r = {};
|
||||
swap(l, r);
|
||||
#if TEST_STD_VER >= 11
|
||||
static_assert(noexcept(swap(l, r)), "");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,10 +15,19 @@
|
|||
#include <cassert>
|
||||
#include <array>
|
||||
|
||||
#include "test_macros.h"
|
||||
|
||||
// std::array is explicitly allowed to be initialized with A a = { init-list };.
|
||||
// Disable the missing braces warning for this reason.
|
||||
#include "disable_missing_braces_warning.h"
|
||||
|
||||
struct NonSwappable {
|
||||
NonSwappable() {}
|
||||
private:
|
||||
NonSwappable(NonSwappable const&);
|
||||
NonSwappable& operator=(NonSwappable const&);
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
{
|
||||
|
@ -70,5 +79,15 @@ int main()
|
|||
assert(c1.size() == 0);
|
||||
assert(c2.size() == 0);
|
||||
}
|
||||
{
|
||||
typedef NonSwappable T;
|
||||
typedef std::array<T, 0> C0;
|
||||
C0 l = {};
|
||||
C0 r = {};
|
||||
l.swap(r);
|
||||
#if TEST_STD_VER >= 11
|
||||
static_assert(noexcept(l.swap(r)), "");
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <memory>
|
||||
#include <cassert>
|
||||
|
||||
#include "test_macros.h"
|
||||
#include "../deleter.h"
|
||||
|
||||
struct A
|
||||
|
@ -34,6 +35,16 @@ struct A
|
|||
|
||||
int A::count = 0;
|
||||
|
||||
template <class T>
|
||||
struct NonSwappableDeleter {
|
||||
explicit NonSwappableDeleter(int) {}
|
||||
NonSwappableDeleter& operator=(NonSwappableDeleter const&) { return *this; }
|
||||
void operator()(T*) const {}
|
||||
private:
|
||||
NonSwappableDeleter(NonSwappableDeleter const&);
|
||||
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
{
|
||||
|
@ -74,4 +85,18 @@ int main()
|
|||
assert(A::count == 6);
|
||||
}
|
||||
assert(A::count == 0);
|
||||
#if TEST_STD_VER >= 11
|
||||
{
|
||||
// test that unique_ptr's specialized swap is disabled when the deleter
|
||||
// is non-swappable. Instead we should pick up the generic swap(T, T)
|
||||
// and perform 3 move constructions.
|
||||
typedef NonSwappableDeleter<int> D;
|
||||
D d(42);
|
||||
int x = 42;
|
||||
int y = 43;
|
||||
std::unique_ptr<int, D&> p(&x, d);
|
||||
std::unique_ptr<int, D&> p2(&y, d);
|
||||
std::swap(p, p2);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++98, c++03, c++11, c++14
|
||||
|
||||
// type_traits
|
||||
|
||||
// is_swappable
|
||||
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
#include "test_macros.h"
|
||||
|
||||
namespace MyNS {
|
||||
|
||||
// Make the test types non-copyable so that generic std::swap is not valid.
|
||||
struct A {
|
||||
A(A const&) = delete;
|
||||
A& operator=(A const&) = delete;
|
||||
};
|
||||
|
||||
struct B {
|
||||
B(B const&) = delete;
|
||||
B& operator=(B const&) = delete;
|
||||
};
|
||||
|
||||
void swap(A&, A&) noexcept {}
|
||||
void swap(B&, B&) {}
|
||||
|
||||
struct M {
|
||||
M(M const&) = delete;
|
||||
M& operator=(M const&) = delete;
|
||||
};
|
||||
|
||||
void swap(M&&, M&&) noexcept {}
|
||||
|
||||
struct ThrowingMove {
|
||||
ThrowingMove(ThrowingMove&&){}
|
||||
ThrowingMove& operator=(ThrowingMove&&) {}
|
||||
};
|
||||
|
||||
} // namespace MyNS
|
||||
|
||||
int main()
|
||||
{
|
||||
using namespace MyNS;
|
||||
{
|
||||
// Test that is_swappable applies an lvalue reference to the type.
|
||||
static_assert(std::is_nothrow_swappable<int>::value, "");
|
||||
static_assert(std::is_nothrow_swappable<int&>::value, "");
|
||||
static_assert(!std::is_nothrow_swappable<M>::value, "");
|
||||
static_assert(!std::is_nothrow_swappable<M&&>::value, "");
|
||||
}
|
||||
{
|
||||
// Test that it correctly deduces the noexcept of swap.
|
||||
static_assert(std::is_nothrow_swappable<A>::value, "");
|
||||
static_assert(!std::is_nothrow_swappable<B>::value
|
||||
&& std::is_swappable<B>::value, "");
|
||||
static_assert(!std::is_nothrow_swappable<ThrowingMove>::value
|
||||
&& std::is_swappable<ThrowingMove>::value);
|
||||
}
|
||||
{
|
||||
// Test that it doesn't drop the qualifiers
|
||||
static_assert(!std::is_nothrow_swappable<const A>::value, "");
|
||||
}
|
||||
{
|
||||
// test non-referenceable types
|
||||
static_assert(!std::is_nothrow_swappable<void>::value, "");
|
||||
static_assert(!std::is_nothrow_swappable<int() const>::value, "");
|
||||
static_assert(!std::is_nothrow_swappable<int(int, ...) const &>::value, "");
|
||||
}
|
||||
{
|
||||
// test for presence of is_nothrow_swappable_v
|
||||
static_assert(std::is_nothrow_swappable_v<int>);
|
||||
static_assert(!std::is_nothrow_swappable_v<void>);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++98, c++03, c++11, c++14
|
||||
|
||||
// type_traits
|
||||
|
||||
// is_nothrow_swappable_with
|
||||
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
#include "test_macros.h"
|
||||
|
||||
namespace MyNS {
|
||||
|
||||
struct A {
|
||||
A(A const&) = delete;
|
||||
A& operator=(A const&) = delete;
|
||||
};
|
||||
|
||||
struct B {
|
||||
B(B const&) = delete;
|
||||
B& operator=(B const&) = delete;
|
||||
};
|
||||
|
||||
struct C {};
|
||||
struct D {};
|
||||
|
||||
void swap(A&, A&) {}
|
||||
|
||||
void swap(A&, B&) noexcept {}
|
||||
void swap(B&, A&) noexcept {}
|
||||
|
||||
void swap(A&, C&) noexcept {}
|
||||
void swap(C&, A&) {}
|
||||
|
||||
struct M {};
|
||||
|
||||
void swap(M&&, M&&) noexcept {}
|
||||
|
||||
} // namespace MyNS
|
||||
|
||||
int main()
|
||||
{
|
||||
using namespace MyNS;
|
||||
{
|
||||
// Test that is_swappable_with doesn't apply an lvalue reference
|
||||
// to the type. Instead it is up to the user.
|
||||
static_assert(!std::is_nothrow_swappable_with<int, int>::value, "");
|
||||
static_assert(std::is_nothrow_swappable_with<int&, int&>::value, "");
|
||||
static_assert(std::is_nothrow_swappable_with<M, M>::value, "");
|
||||
static_assert(std::is_swappable_with<A&, A&>::value &&
|
||||
!std::is_nothrow_swappable_with<A&, A&>::value, "");
|
||||
}
|
||||
{
|
||||
// test that hetrogenius swap is allowed only if both 'swap(A, B)' and
|
||||
// 'swap(B, A)' are valid.
|
||||
static_assert(std::is_nothrow_swappable_with<A&, B&>::value, "");
|
||||
static_assert(!std::is_nothrow_swappable_with<A&, C&>::value &&
|
||||
std::is_swappable_with<A&, C&>::value, "");
|
||||
static_assert(!std::is_nothrow_swappable_with<D&, C&>::value, "");
|
||||
}
|
||||
{
|
||||
// test we guard against cv void inputs as required.
|
||||
static_assert(!std::is_nothrow_swappable_with_v<void, int>);
|
||||
static_assert(!std::is_nothrow_swappable_with_v<int, void>);
|
||||
static_assert(!std::is_nothrow_swappable_with_v<const void, const volatile void>);
|
||||
|
||||
}
|
||||
{
|
||||
// test for presense of is_nothrow_swappable_with_v
|
||||
static_assert(std::is_nothrow_swappable_with_v<int&, int&>);
|
||||
static_assert(!std::is_nothrow_swappable_with_v<int&&, int&&>);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++98, c++03, c++11, c++14
|
||||
|
||||
// type_traits
|
||||
|
||||
// is_swappable
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "test_macros.h"
|
||||
|
||||
namespace MyNS {
|
||||
|
||||
// Make the test types non-copyable so that generic std::swap is not valid.
|
||||
struct A {
|
||||
A(A const&) = delete;
|
||||
A& operator=(A const&) = delete;
|
||||
};
|
||||
|
||||
struct B {
|
||||
B(B const&) = delete;
|
||||
B& operator=(B const&) = delete;
|
||||
};
|
||||
|
||||
struct C {};
|
||||
struct D {};
|
||||
|
||||
void swap(A&, A&) {}
|
||||
|
||||
void swap(A&, B&) {}
|
||||
void swap(B&, A&) {}
|
||||
|
||||
void swap(A&, C&) {} // missing swap(C, A)
|
||||
void swap(D&, C&) {}
|
||||
|
||||
struct M {
|
||||
M(M const&) = delete;
|
||||
M& operator=(M const&) = delete;
|
||||
};
|
||||
|
||||
void swap(M&&, M&&) {}
|
||||
|
||||
} // namespace MyNS
|
||||
|
||||
int main()
|
||||
{
|
||||
using namespace MyNS;
|
||||
{
|
||||
// Test that is_swappable applies an lvalue reference to the type.
|
||||
static_assert(std::is_swappable<A>::value, "");
|
||||
static_assert(std::is_swappable<A&>::value, "");
|
||||
static_assert(!std::is_swappable<M>::value, "");
|
||||
static_assert(!std::is_swappable<M&&>::value, "");
|
||||
}
|
||||
static_assert(!std::is_swappable<B>::value, "");
|
||||
static_assert(std::is_swappable<C>::value, "");
|
||||
{
|
||||
// test non-referencable types
|
||||
static_assert(!std::is_swappable<void>::value, "");
|
||||
static_assert(!std::is_swappable<int() const>::value, "");
|
||||
static_assert(!std::is_swappable<int() &>::value, "");
|
||||
}
|
||||
{
|
||||
// test for presense of is_swappable_v
|
||||
static_assert(std::is_swappable_v<int>);
|
||||
static_assert(!std::is_swappable_v<M>);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// type_traits
|
||||
|
||||
// is_swappable
|
||||
|
||||
// IMPORTANT: The include order is part of the test. We want to pick up
|
||||
// the following definitions in this order:
|
||||
// 1) is_swappable, is_nothrow_swappable
|
||||
// 2) iter_swap, swap_ranges
|
||||
// 3) swap(T (&)[N], T(&)[N]
|
||||
// This test checks that (1) and (2) see forward declarations
|
||||
// for (3).
|
||||
#include <type_traits>
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
#include "test_macros.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
// Use a builtin type so we don't get ADL lookup.
|
||||
typedef double T[42][50];
|
||||
{
|
||||
static_assert(std::__is_swappable<T>::value, "");
|
||||
#if TEST_STD_VER > 14
|
||||
static_assert(std::is_swappable_v<T>);
|
||||
#endif
|
||||
}
|
||||
{
|
||||
T t1 = {};
|
||||
T t2 = {};
|
||||
std::iter_swap(t1, t2);
|
||||
std::swap_ranges(t1, t1 + 42, t2);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++98, c++03, c++11, c++14
|
||||
|
||||
// type_traits
|
||||
|
||||
// is_swappable_with
|
||||
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
#include "test_macros.h"
|
||||
|
||||
namespace MyNS {
|
||||
|
||||
struct A {
|
||||
A(A const&) = delete;
|
||||
A& operator=(A const&) = delete;
|
||||
};
|
||||
|
||||
struct B {
|
||||
B(B const&) = delete;
|
||||
B& operator=(B const&) = delete;
|
||||
};
|
||||
|
||||
struct C {};
|
||||
struct D {};
|
||||
|
||||
void swap(A&, A&) {}
|
||||
|
||||
void swap(A&, B&) {}
|
||||
void swap(B&, A&) {}
|
||||
|
||||
void swap(A&, C&) {} // missing swap(C, A)
|
||||
void swap(D&, C&) {}
|
||||
|
||||
struct M {};
|
||||
|
||||
void swap(M&&, M&&) {}
|
||||
|
||||
} // namespace MyNS
|
||||
|
||||
int main()
|
||||
{
|
||||
using namespace MyNS;
|
||||
{
|
||||
// Test that is_swappable_with doesn't apply an lvalue reference
|
||||
// to the type. Instead it is up to the user.
|
||||
static_assert(!std::is_swappable_with<int, int>::value, "");
|
||||
static_assert(std::is_swappable_with<int&, int&>::value, "");
|
||||
static_assert(std::is_swappable_with<M, M>::value, "");
|
||||
static_assert(std::is_swappable_with<A&, A&>::value, "");
|
||||
}
|
||||
{
|
||||
// test that heterogeneous swap is allowed only if both 'swap(A, B)' and
|
||||
// 'swap(B, A)' are valid.
|
||||
static_assert(std::is_swappable_with<A&, B&>::value, "");
|
||||
static_assert(!std::is_swappable_with<A&, C&>::value, "");
|
||||
static_assert(!std::is_swappable_with<D&, C&>::value, "");
|
||||
}
|
||||
{
|
||||
// test that cv void is guarded against as required.
|
||||
static_assert(!std::is_swappable_with_v<void, int>);
|
||||
static_assert(!std::is_swappable_with_v<int, void>);
|
||||
static_assert(!std::is_swappable_with_v<const void, const volatile void>);
|
||||
}
|
||||
{
|
||||
// test for presence of is_swappable_with_v
|
||||
static_assert(std::is_swappable_with_v<int&, int&>);
|
||||
static_assert(!std::is_swappable_with_v<D&, C&>);
|
||||
}
|
||||
}
|
|
@ -16,38 +16,88 @@
|
|||
|
||||
#include <utility>
|
||||
#include <cassert>
|
||||
#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES
|
||||
#include <memory>
|
||||
|
||||
#include "test_macros.h"
|
||||
|
||||
#if TEST_STD_VER >= 11
|
||||
struct CopyOnly {
|
||||
CopyOnly() {}
|
||||
CopyOnly(CopyOnly const&) noexcept {}
|
||||
CopyOnly& operator=(CopyOnly const&) { return *this; }
|
||||
};
|
||||
|
||||
struct MoveOnly {
|
||||
MoveOnly() {}
|
||||
MoveOnly(MoveOnly&&) {}
|
||||
MoveOnly& operator=(MoveOnly&&) noexcept { return *this; }
|
||||
};
|
||||
|
||||
struct NoexceptMoveOnly {
|
||||
NoexceptMoveOnly() {}
|
||||
NoexceptMoveOnly(NoexceptMoveOnly&&) noexcept {}
|
||||
NoexceptMoveOnly& operator=(NoexceptMoveOnly&&) noexcept { return *this; }
|
||||
};
|
||||
|
||||
struct NotMoveConstructible {
|
||||
NotMoveConstructible& operator=(NotMoveConstructible&&) { return *this; }
|
||||
private:
|
||||
NotMoveConstructible(NotMoveConstructible&&);
|
||||
};
|
||||
|
||||
struct NotMoveAssignable {
|
||||
NotMoveAssignable(NotMoveAssignable&&);
|
||||
private:
|
||||
NotMoveAssignable& operator=(NotMoveAssignable&&);
|
||||
};
|
||||
|
||||
template <class Tp>
|
||||
auto can_swap_test(int) -> decltype(std::swap(std::declval<Tp>(), std::declval<Tp>()));
|
||||
|
||||
template <class Tp>
|
||||
auto can_swap_test(...) -> std::false_type;
|
||||
|
||||
template <class Tp>
|
||||
constexpr bool can_swap() {
|
||||
return std::is_same<decltype(can_swap_test<Tp>(0)), void>::value;
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
test()
|
||||
{
|
||||
int i = 1;
|
||||
int j = 2;
|
||||
std::swap(i, j);
|
||||
assert(i == 2);
|
||||
assert(j == 1);
|
||||
}
|
||||
|
||||
#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES
|
||||
|
||||
void
|
||||
test1()
|
||||
{
|
||||
std::unique_ptr<int> i(new int(1));
|
||||
std::unique_ptr<int> j(new int(2));
|
||||
std::swap(i, j);
|
||||
assert(*i == 2);
|
||||
assert(*j == 1);
|
||||
}
|
||||
|
||||
#endif // _LIBCPP_HAS_NO_RVALUE_REFERENCES
|
||||
|
||||
int main()
|
||||
{
|
||||
test();
|
||||
#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES
|
||||
test1();
|
||||
|
||||
{
|
||||
int i = 1;
|
||||
int j = 2;
|
||||
std::swap(i, j);
|
||||
assert(i == 2);
|
||||
assert(j == 1);
|
||||
}
|
||||
#if TEST_STD_VER >= 11
|
||||
{
|
||||
|
||||
std::unique_ptr<int> i(new int(1));
|
||||
std::unique_ptr<int> j(new int(2));
|
||||
std::swap(i, j);
|
||||
assert(*i == 2);
|
||||
assert(*j == 1);
|
||||
|
||||
}
|
||||
{
|
||||
// test that the swap
|
||||
static_assert(can_swap<CopyOnly&>(), "");
|
||||
static_assert(can_swap<MoveOnly&>(), "");
|
||||
static_assert(can_swap<NoexceptMoveOnly&>(), "");
|
||||
|
||||
static_assert(!can_swap<NotMoveConstructible&>(), "");
|
||||
static_assert(!can_swap<NotMoveAssignable&>(), "");
|
||||
|
||||
CopyOnly c;
|
||||
MoveOnly m;
|
||||
NoexceptMoveOnly nm;
|
||||
static_assert(!noexcept(std::swap(c, c)), "");
|
||||
static_assert(!noexcept(std::swap(m, m)), "");
|
||||
static_assert(noexcept(std::swap(nm, nm)), "");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -16,50 +16,86 @@
|
|||
|
||||
#include <utility>
|
||||
#include <cassert>
|
||||
#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES
|
||||
#include <memory>
|
||||
|
||||
#include "test_macros.h"
|
||||
|
||||
|
||||
#if TEST_STD_VER >= 11
|
||||
struct CopyOnly {
|
||||
CopyOnly() {}
|
||||
CopyOnly(CopyOnly const&) noexcept {}
|
||||
CopyOnly& operator=(CopyOnly const&) { return *this; }
|
||||
};
|
||||
|
||||
|
||||
struct NoexceptMoveOnly {
|
||||
NoexceptMoveOnly() {}
|
||||
NoexceptMoveOnly(NoexceptMoveOnly&&) noexcept {}
|
||||
NoexceptMoveOnly& operator=(NoexceptMoveOnly&&) noexcept { return *this; }
|
||||
};
|
||||
|
||||
struct NotMoveConstructible {
|
||||
NotMoveConstructible() {}
|
||||
NotMoveConstructible& operator=(NotMoveConstructible&&) { return *this; }
|
||||
private:
|
||||
NotMoveConstructible(NotMoveConstructible&&);
|
||||
};
|
||||
|
||||
template <class Tp>
|
||||
auto can_swap_test(int) -> decltype(std::swap(std::declval<Tp>(), std::declval<Tp>()));
|
||||
|
||||
template <class Tp>
|
||||
auto can_swap_test(...) -> std::false_type;
|
||||
|
||||
template <class Tp>
|
||||
constexpr bool can_swap() {
|
||||
return std::is_same<decltype(can_swap_test<Tp>(0)), void>::value;
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
test()
|
||||
{
|
||||
int i[3] = {1, 2, 3};
|
||||
int j[3] = {4, 5, 6};
|
||||
std::swap(i, j);
|
||||
assert(i[0] == 4);
|
||||
assert(i[1] == 5);
|
||||
assert(i[2] == 6);
|
||||
assert(j[0] == 1);
|
||||
assert(j[1] == 2);
|
||||
assert(j[2] == 3);
|
||||
}
|
||||
|
||||
#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES
|
||||
|
||||
void
|
||||
test1()
|
||||
{
|
||||
std::unique_ptr<int> i[3];
|
||||
for (int k = 0; k < 3; ++k)
|
||||
i[k].reset(new int(k+1));
|
||||
std::unique_ptr<int> j[3];
|
||||
for (int k = 0; k < 3; ++k)
|
||||
j[k].reset(new int(k+4));
|
||||
std::swap(i, j);
|
||||
assert(*i[0] == 4);
|
||||
assert(*i[1] == 5);
|
||||
assert(*i[2] == 6);
|
||||
assert(*j[0] == 1);
|
||||
assert(*j[1] == 2);
|
||||
assert(*j[2] == 3);
|
||||
}
|
||||
|
||||
#endif // _LIBCPP_HAS_NO_RVALUE_REFERENCES
|
||||
|
||||
int main()
|
||||
{
|
||||
test();
|
||||
#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES
|
||||
test1();
|
||||
{
|
||||
int i[3] = {1, 2, 3};
|
||||
int j[3] = {4, 5, 6};
|
||||
std::swap(i, j);
|
||||
assert(i[0] == 4);
|
||||
assert(i[1] == 5);
|
||||
assert(i[2] == 6);
|
||||
assert(j[0] == 1);
|
||||
assert(j[1] == 2);
|
||||
assert(j[2] == 3);
|
||||
}
|
||||
#if TEST_STD_VER >= 11
|
||||
{
|
||||
std::unique_ptr<int> i[3];
|
||||
for (int k = 0; k < 3; ++k)
|
||||
i[k].reset(new int(k+1));
|
||||
std::unique_ptr<int> j[3];
|
||||
for (int k = 0; k < 3; ++k)
|
||||
j[k].reset(new int(k+4));
|
||||
std::swap(i, j);
|
||||
assert(*i[0] == 4);
|
||||
assert(*i[1] == 5);
|
||||
assert(*i[2] == 6);
|
||||
assert(*j[0] == 1);
|
||||
assert(*j[1] == 2);
|
||||
assert(*j[2] == 3);
|
||||
}
|
||||
{
|
||||
using CA = CopyOnly[42];
|
||||
using MA = NoexceptMoveOnly[42];
|
||||
using NA = NotMoveConstructible[42];
|
||||
static_assert(can_swap<CA&>(), "");
|
||||
static_assert(can_swap<MA&>(), "");
|
||||
static_assert(!can_swap<NA&>(), "");
|
||||
|
||||
CA ca;
|
||||
MA ma;
|
||||
static_assert(!noexcept(std::swap(ca, ca)), "");
|
||||
static_assert(noexcept(std::swap(ma, ma)), "");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -85,7 +85,7 @@
|
|||
<tr><td><a href="http://wg21.link/P0033R1">P0033R1</a></td><td>LWG</td><td>Re-enabling shared_from_this</td><td>Jacksonville</td><td></td><td></td></tr>
|
||||
<tr><td><a href="http://wg21.link/P0005R4">P0005R4</a></td><td>LWG</td><td>Adopt not_fn from Library Fundamentals 2 for C++17</td><td>Jacksonville</td><td></td><td></td></tr>
|
||||
<tr><td><a href="http://wg21.link/P0152R1">P0152R1</a></td><td>LWG</td><td>constexpr atomic::is_always_lock_free</td><td>Jacksonville</td><td>Complete</td><td>3.9</td></tr>
|
||||
<tr><td><a href="http://wg21.link/P0185R1">P0185R1</a></td><td>LWG</td><td>Adding [nothrow-]swappable traits</td><td>Jacksonville</td><td></td><td></td></tr>
|
||||
<tr><td><a href="http://wg21.link/P0185R1">P0185R1</a></td><td>LWG</td><td>Adding [nothrow-]swappable traits</td><td>Jacksonville</td><td>Complete</td><td>3.9</td></tr>
|
||||
<tr><td><a href="http://wg21.link/P0253R1">P0253R1</a></td><td>LWG</td><td>Fixing a design mistake in the searchers interface</td><td>Jacksonville</td><td>Complete</td><td>3.9</td></tr>
|
||||
<tr><td><a href="http://wg21.link/P0025R0">P0025R0</a></td><td>LWG</td><td>An algorithm to "clamp" a value between a pair of boundary values</td><td>Jacksonville</td><td>Complete</td><td>3.9</td></tr>
|
||||
<tr><td><a href="http://wg21.link/P0154R1">P0154R1</a></td><td>LWG</td><td>constexpr std::hardware_{constructive,destructive}_interference_size</td><td>Jacksonville</td><td></td><td></td></tr>
|
||||
|
|
Loading…
Reference in New Issue