[libcxx][NFC] Make sequence containers slightly more SFINAE-friendly during CTAD.

Disable the constructors taking `(size_type, const value_type&,
allocator_type)` if `allocator_type` is not a valid allocator.
Otherwise, these constructors are considered when resolving e.g.
`(int*, int*, NotAnAllocator())`, leading to a hard error during
instantiation. A hard error makes the Standard's requirement to not
consider deduction guides of the form `(Iterator, Iterator,
BadAllocator)` during overload resolution essentially non-functional.

The previous approach was to SFINAE away `allocator_traits`. This patch
SFINAEs away the specific constructors instead, for consistency with
`basic_string` -- see [LWG3076](wg21.link/lwg3076) which describes
a very similar problem for strings (note, however, that unlike LWG3076,
no valid constructor call is affected by the bad instantiation).

Differential Revision: https://reviews.llvm.org/D114311
This commit is contained in:
Konstantin Varlamov 2021-12-01 11:55:46 -08:00
parent 9e647806f3
commit 7da4ee6f23
25 changed files with 124 additions and 110 deletions

View File

@ -349,14 +349,6 @@ struct _LIBCPP_TEMPLATE_VIS allocator_traits
} }
}; };
// A version of `allocator_traits` for internal usage that SFINAEs away if the
// given allocator doesn't have a nested `value_type`. This helps avoid hard
// errors when forming implicit deduction guides for a container that has an
// invalid Allocator type. See https://wg21.link/LWGXXXXX.
// TODO(varconst): use the actual link once available.
template <class _Alloc, class _ValueType = typename _Alloc::value_type>
struct _LIBCPP_TEMPLATE_VIS __allocator_traits : allocator_traits<_Alloc> {};
template <class _Traits, class _Tp> template <class _Traits, class _Tp>
struct __rebind_alloc_helper { struct __rebind_alloc_helper {
#ifndef _LIBCPP_CXX03_LANG #ifndef _LIBCPP_CXX03_LANG

View File

@ -915,16 +915,16 @@ class __deque_base
__deque_base(const __deque_base& __c); __deque_base(const __deque_base& __c);
__deque_base& operator=(const __deque_base& __c); __deque_base& operator=(const __deque_base& __c);
public: public:
typedef _Allocator allocator_type; typedef _Allocator allocator_type;
typedef allocator_traits<allocator_type> __alloc_traits; typedef allocator_traits<allocator_type> __alloc_traits;
typedef typename __alloc_traits::size_type size_type; typedef typename __alloc_traits::size_type size_type;
typedef _Tp value_type; typedef _Tp value_type;
typedef value_type& reference; typedef value_type& reference;
typedef const value_type& const_reference; typedef const value_type& const_reference;
typedef typename __alloc_traits::difference_type difference_type; typedef typename __alloc_traits::difference_type difference_type;
typedef typename __alloc_traits::pointer pointer; typedef typename __alloc_traits::pointer pointer;
typedef typename __alloc_traits::const_pointer const_pointer; typedef typename __alloc_traits::const_pointer const_pointer;
static const difference_type __block_size; static const difference_type __block_size;
@ -1259,20 +1259,20 @@ public:
static_assert((is_same<typename allocator_type::value_type, value_type>::value), static_assert((is_same<typename allocator_type::value_type, value_type>::value),
"Allocator::value_type must be same type as value_type"); "Allocator::value_type must be same type as value_type");
typedef __deque_base<value_type, allocator_type> __base; typedef __deque_base<value_type, allocator_type> __base;
typedef typename __base::__alloc_traits __alloc_traits; typedef typename __base::__alloc_traits __alloc_traits;
typedef typename __base::reference reference; typedef typename __base::reference reference;
typedef typename __base::const_reference const_reference; typedef typename __base::const_reference const_reference;
typedef typename __base::iterator iterator; typedef typename __base::iterator iterator;
typedef typename __base::const_iterator const_iterator; typedef typename __base::const_iterator const_iterator;
typedef typename __allocator_traits<allocator_type>::size_type size_type; typedef typename __base::size_type size_type;
typedef typename __base::difference_type difference_type; typedef typename __base::difference_type difference_type;
typedef typename __base::pointer pointer; typedef typename __base::pointer pointer;
typedef typename __base::const_pointer const_pointer; typedef typename __base::const_pointer const_pointer;
typedef _VSTD::reverse_iterator<iterator> reverse_iterator; typedef _VSTD::reverse_iterator<iterator> reverse_iterator;
typedef _VSTD::reverse_iterator<const_iterator> const_reverse_iterator; typedef _VSTD::reverse_iterator<const_iterator> const_reverse_iterator;
using typename __base::__deque_range; using typename __base::__deque_range;
using typename __base::__deque_block_range; using typename __base::__deque_block_range;
@ -1289,7 +1289,14 @@ public:
explicit deque(size_type __n, const _Allocator& __a); explicit deque(size_type __n, const _Allocator& __a);
#endif #endif
deque(size_type __n, const value_type& __v); deque(size_type __n, const value_type& __v);
deque(size_type __n, const value_type& __v, const allocator_type& __a);
template <class = __enable_if_t<__is_allocator<_Allocator>::value> >
deque(size_type __n, const value_type& __v, const allocator_type& __a) : __base(__a)
{
if (__n > 0)
__append(__n, __v);
}
template <class _InputIter> template <class _InputIter>
deque(_InputIter __f, _InputIter __l, deque(_InputIter __f, _InputIter __l,
typename enable_if<__is_cpp17_input_iterator<_InputIter>::value>::type* = 0); typename enable_if<__is_cpp17_input_iterator<_InputIter>::value>::type* = 0);
@ -1608,14 +1615,6 @@ deque<_Tp, _Allocator>::deque(size_type __n, const value_type& __v)
__append(__n, __v); __append(__n, __v);
} }
template <class _Tp, class _Allocator>
deque<_Tp, _Allocator>::deque(size_type __n, const value_type& __v, const allocator_type& __a)
: __base(__a)
{
if (__n > 0)
__append(__n, __v);
}
template <class _Tp, class _Allocator> template <class _Tp, class _Allocator>
template <class _InputIter> template <class _InputIter>
deque<_Tp, _Allocator>::deque(_InputIter __f, _InputIter __l, deque<_Tp, _Allocator>::deque(_InputIter __f, _InputIter __l,

View File

@ -186,6 +186,7 @@ template <class T, class Allocator, class Predicate>
#include <iterator> #include <iterator>
#include <limits> #include <limits>
#include <memory> #include <memory>
#include <type_traits>
#include <version> #include <version>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@ -643,12 +644,12 @@ public:
static_assert((is_same<typename allocator_type::value_type, value_type>::value), static_assert((is_same<typename allocator_type::value_type, value_type>::value),
"Allocator::value_type must be same type as value_type"); "Allocator::value_type must be same type as value_type");
typedef value_type& reference; typedef value_type& reference;
typedef const value_type& const_reference; typedef const value_type& const_reference;
typedef typename allocator_traits<allocator_type>::pointer pointer; typedef typename allocator_traits<allocator_type>::pointer pointer;
typedef typename allocator_traits<allocator_type>::const_pointer const_pointer; typedef typename allocator_traits<allocator_type>::const_pointer const_pointer;
typedef typename __allocator_traits<allocator_type>::size_type size_type; typedef typename allocator_traits<allocator_type>::size_type size_type;
typedef typename allocator_traits<allocator_type>::difference_type difference_type; typedef typename allocator_traits<allocator_type>::difference_type difference_type;
typedef typename base::iterator iterator; typedef typename base::iterator iterator;
typedef typename base::const_iterator const_iterator; typedef typename base::const_iterator const_iterator;
@ -669,7 +670,13 @@ public:
explicit forward_list(size_type __n, const allocator_type& __a); explicit forward_list(size_type __n, const allocator_type& __a);
#endif #endif
forward_list(size_type __n, const value_type& __v); forward_list(size_type __n, const value_type& __v);
forward_list(size_type __n, const value_type& __v, const allocator_type& __a);
template <class = __enable_if_t<__is_allocator<_Alloc>::value> >
forward_list(size_type __n, const value_type& __v, const allocator_type& __a) : base(__a)
{
insert_after(cbefore_begin(), __n, __v);
}
template <class _InputIterator> template <class _InputIterator>
forward_list(_InputIterator __f, _InputIterator __l, forward_list(_InputIterator __f, _InputIterator __l,
typename enable_if< typename enable_if<
@ -943,14 +950,6 @@ forward_list<_Tp, _Alloc>::forward_list(size_type __n, const value_type& __v)
insert_after(cbefore_begin(), __n, __v); insert_after(cbefore_begin(), __n, __v);
} }
template <class _Tp, class _Alloc>
forward_list<_Tp, _Alloc>::forward_list(size_type __n, const value_type& __v,
const allocator_type& __a)
: base(__a)
{
insert_after(cbefore_begin(), __n, __v);
}
template <class _Tp, class _Alloc> template <class _Tp, class _Alloc>
template <class _InputIterator> template <class _InputIterator>
forward_list<_Tp, _Alloc>::forward_list(_InputIterator __f, _InputIterator __l, forward_list<_Tp, _Alloc>::forward_list(_InputIterator __f, _InputIterator __l,

View File

@ -845,24 +845,24 @@ class _LIBCPP_TEMPLATE_VIS list
typedef typename base::__link_pointer __link_pointer; typedef typename base::__link_pointer __link_pointer;
public: public:
typedef _Tp value_type; typedef _Tp value_type;
typedef _Alloc allocator_type; typedef _Alloc allocator_type;
static_assert((is_same<value_type, typename allocator_type::value_type>::value), static_assert((is_same<value_type, typename allocator_type::value_type>::value),
"Invalid allocator::value_type"); "Invalid allocator::value_type");
typedef value_type& reference; typedef value_type& reference;
typedef const value_type& const_reference; typedef const value_type& const_reference;
typedef typename base::pointer pointer; typedef typename base::pointer pointer;
typedef typename base::const_pointer const_pointer; typedef typename base::const_pointer const_pointer;
typedef typename __allocator_traits<allocator_type>::size_type size_type; typedef typename base::size_type size_type;
typedef typename base::difference_type difference_type; typedef typename base::difference_type difference_type;
typedef typename base::iterator iterator; typedef typename base::iterator iterator;
typedef typename base::const_iterator const_iterator; typedef typename base::const_iterator const_iterator;
typedef _VSTD::reverse_iterator<iterator> reverse_iterator; typedef _VSTD::reverse_iterator<iterator> reverse_iterator;
typedef _VSTD::reverse_iterator<const_iterator> const_reverse_iterator; typedef _VSTD::reverse_iterator<const_iterator> const_reverse_iterator;
#if _LIBCPP_STD_VER > 17 #if _LIBCPP_STD_VER > 17
typedef size_type __remove_return_type; typedef size_type __remove_return_type;
#else #else
typedef void __remove_return_type; typedef void __remove_return_type;
#endif #endif
_LIBCPP_INLINE_VISIBILITY _LIBCPP_INLINE_VISIBILITY
@ -885,7 +885,16 @@ public:
explicit list(size_type __n, const allocator_type& __a); explicit list(size_type __n, const allocator_type& __a);
#endif #endif
list(size_type __n, const value_type& __x); list(size_type __n, const value_type& __x);
list(size_type __n, const value_type& __x, const allocator_type& __a); template <class = __enable_if_t<__is_allocator<_Alloc>::value> >
list(size_type __n, const value_type& __x, const allocator_type& __a) : base(__a)
{
#if _LIBCPP_DEBUG_LEVEL == 2
__get_db()->__insert_c(this);
#endif
for (; __n > 0; --__n)
push_back(__x);
}
template <class _InpIter> template <class _InpIter>
list(_InpIter __f, _InpIter __l, list(_InpIter __f, _InpIter __l,
typename enable_if<__is_cpp17_input_iterator<_InpIter>::value>::type* = 0); typename enable_if<__is_cpp17_input_iterator<_InpIter>::value>::type* = 0);
@ -1241,17 +1250,6 @@ list<_Tp, _Alloc>::list(size_type __n, const value_type& __x)
push_back(__x); push_back(__x);
} }
template <class _Tp, class _Alloc>
list<_Tp, _Alloc>::list(size_type __n, const value_type& __x, const allocator_type& __a)
: base(__a)
{
#if _LIBCPP_DEBUG_LEVEL == 2
__get_db()->__insert_c(this);
#endif
for (; __n > 0; --__n)
push_back(__x);
}
template <class _Tp, class _Alloc> template <class _Tp, class _Alloc>
template <class _InpIter> template <class _InpIter>
list<_Tp, _Alloc>::list(_InpIter __f, _InpIter __l, list<_Tp, _Alloc>::list(_InpIter __f, _InpIter __l,

View File

@ -350,23 +350,23 @@ class _LIBCPP_TEMPLATE_VIS vector
: private __vector_base<_Tp, _Allocator> : private __vector_base<_Tp, _Allocator>
{ {
private: private:
typedef __vector_base<_Tp, _Allocator> __base; typedef __vector_base<_Tp, _Allocator> __base;
typedef allocator<_Tp> __default_allocator_type; typedef allocator<_Tp> __default_allocator_type;
public: public:
typedef vector __self; typedef vector __self;
typedef _Tp value_type; typedef _Tp value_type;
typedef _Allocator allocator_type; typedef _Allocator allocator_type;
typedef allocator_traits<allocator_type> __alloc_traits; typedef allocator_traits<allocator_type> __alloc_traits;
typedef value_type& reference; typedef value_type& reference;
typedef const value_type& const_reference; typedef const value_type& const_reference;
typedef typename __allocator_traits<allocator_type>::size_type size_type; typedef typename __alloc_traits::size_type size_type;
typedef typename __alloc_traits::difference_type difference_type; typedef typename __alloc_traits::difference_type difference_type;
typedef typename __alloc_traits::pointer pointer; typedef typename __alloc_traits::pointer pointer;
typedef typename __alloc_traits::const_pointer const_pointer; typedef typename __alloc_traits::const_pointer const_pointer;
typedef __wrap_iter<pointer> iterator; typedef __wrap_iter<pointer> iterator;
typedef __wrap_iter<const_pointer> const_iterator; typedef __wrap_iter<const_pointer> const_iterator;
typedef _VSTD::reverse_iterator<iterator> reverse_iterator; typedef _VSTD::reverse_iterator<iterator> reverse_iterator;
typedef _VSTD::reverse_iterator<const_iterator> const_reverse_iterator; typedef _VSTD::reverse_iterator<const_iterator> const_reverse_iterator;
static_assert((is_same<typename allocator_type::value_type, value_type>::value), static_assert((is_same<typename allocator_type::value_type, value_type>::value),
"Allocator::value_type must be same type as value_type"); "Allocator::value_type must be same type as value_type");
@ -395,7 +395,21 @@ public:
explicit vector(size_type __n, const allocator_type& __a); explicit vector(size_type __n, const allocator_type& __a);
#endif #endif
vector(size_type __n, const value_type& __x); vector(size_type __n, const value_type& __x);
vector(size_type __n, const value_type& __x, const allocator_type& __a);
template <class = __enable_if_t<__is_allocator<_Allocator>::value> >
vector(size_type __n, const value_type& __x, const allocator_type& __a)
: __base(__a)
{
#if _LIBCPP_DEBUG_LEVEL == 2
__get_db()->__insert_c(this);
#endif
if (__n > 0)
{
__vallocate(__n);
__construct_at_end(__n, __x);
}
}
template <class _InputIterator> template <class _InputIterator>
vector(_InputIterator __first, vector(_InputIterator __first,
typename enable_if<__is_cpp17_input_iterator <_InputIterator>::value && typename enable_if<__is_cpp17_input_iterator <_InputIterator>::value &&
@ -1126,20 +1140,6 @@ vector<_Tp, _Allocator>::vector(size_type __n, const value_type& __x)
} }
} }
template <class _Tp, class _Allocator>
vector<_Tp, _Allocator>::vector(size_type __n, const value_type& __x, const allocator_type& __a)
: __base(__a)
{
#if _LIBCPP_DEBUG_LEVEL == 2
__get_db()->__insert_c(this);
#endif
if (__n > 0)
{
__vallocate(__n);
__construct_at_end(__n, __x);
}
}
template <class _Tp, class _Allocator> template <class _Tp, class _Allocator>
template <class _InputIterator> template <class _InputIterator>
vector<_Tp, _Allocator>::vector(_InputIterator __first, vector<_Tp, _Allocator>::vector(_InputIterator __first,

View File

@ -27,6 +27,7 @@ struct some_alloc
{ {
typedef T value_type; typedef T value_type;
some_alloc(const some_alloc&); some_alloc(const some_alloc&);
void allocate(size_t);
}; };
int main(int, char**) int main(int, char**)

View File

@ -25,6 +25,7 @@ struct some_alloc
typedef T value_type; typedef T value_type;
some_alloc(const some_alloc&); some_alloc(const some_alloc&);
~some_alloc() noexcept(false); ~some_alloc() noexcept(false);
void allocate(size_t);
}; };
int main(int, char**) int main(int, char**)

View File

@ -29,6 +29,7 @@ struct some_alloc
{ {
typedef T value_type; typedef T value_type;
some_alloc(const some_alloc&); some_alloc(const some_alloc&);
void allocate(size_t);
}; };
int main(int, char**) int main(int, char**)

View File

@ -27,6 +27,7 @@ struct some_alloc
{ {
typedef T value_type; typedef T value_type;
some_alloc(const some_alloc&); some_alloc(const some_alloc&);
void allocate(size_t);
}; };
int main(int, char**) int main(int, char**)

View File

@ -34,6 +34,7 @@ struct some_alloc
some_alloc() {} some_alloc() {}
some_alloc(const some_alloc&); some_alloc(const some_alloc&);
void allocate(size_t);
void deallocate(void*, unsigned) {} void deallocate(void*, unsigned) {}
typedef std::true_type propagate_on_container_swap; typedef std::true_type propagate_on_container_swap;
@ -46,6 +47,7 @@ struct some_alloc2
some_alloc2() {} some_alloc2() {}
some_alloc2(const some_alloc2&); some_alloc2(const some_alloc2&);
void allocate(size_t);
void deallocate(void*, unsigned) {} void deallocate(void*, unsigned) {}
typedef std::false_type propagate_on_container_swap; typedef std::false_type propagate_on_container_swap;

View File

@ -27,6 +27,7 @@ struct some_alloc
{ {
typedef T value_type; typedef T value_type;
some_alloc(const some_alloc&); some_alloc(const some_alloc&);
void allocate(size_t);
}; };
int main(int, char**) int main(int, char**)

View File

@ -25,6 +25,7 @@ struct some_alloc
typedef T value_type; typedef T value_type;
some_alloc(const some_alloc&); some_alloc(const some_alloc&);
~some_alloc() noexcept(false); ~some_alloc() noexcept(false);
void allocate(size_t);
}; };
int main(int, char**) int main(int, char**)

View File

@ -29,6 +29,7 @@ struct some_alloc
{ {
typedef T value_type; typedef T value_type;
some_alloc(const some_alloc&); some_alloc(const some_alloc&);
void allocate(size_t);
}; };
int main(int, char**) int main(int, char**)

View File

@ -27,6 +27,7 @@ struct some_alloc
{ {
typedef T value_type; typedef T value_type;
some_alloc(const some_alloc&); some_alloc(const some_alloc&);
void allocate(size_t);
}; };
int main(int, char**) int main(int, char**)

View File

@ -35,6 +35,7 @@ struct some_alloc
some_alloc() {} some_alloc() {}
some_alloc(const some_alloc&); some_alloc(const some_alloc&);
void allocate(size_t);
void deallocate(void*, unsigned) {} void deallocate(void*, unsigned) {}
typedef std::true_type propagate_on_container_swap; typedef std::true_type propagate_on_container_swap;
@ -47,6 +48,7 @@ struct some_alloc2
some_alloc2() {} some_alloc2() {}
some_alloc2(const some_alloc2&); some_alloc2(const some_alloc2&);
void allocate(size_t);
void deallocate(void*, unsigned) {} void deallocate(void*, unsigned) {}
typedef std::false_type propagate_on_container_swap; typedef std::false_type propagate_on_container_swap;

View File

@ -27,6 +27,7 @@ struct some_alloc
{ {
typedef T value_type; typedef T value_type;
some_alloc(const some_alloc&); some_alloc(const some_alloc&);
void allocate(size_t);
}; };
int main(int, char**) int main(int, char**)

View File

@ -25,6 +25,7 @@ struct some_alloc
typedef T value_type; typedef T value_type;
some_alloc(const some_alloc&); some_alloc(const some_alloc&);
~some_alloc() noexcept(false); ~some_alloc() noexcept(false);
void allocate(size_t);
}; };
int main(int, char**) int main(int, char**)

View File

@ -29,6 +29,7 @@ struct some_alloc
{ {
typedef T value_type; typedef T value_type;
some_alloc(const some_alloc&); some_alloc(const some_alloc&);
void allocate(size_t);
}; };
int main(int, char**) int main(int, char**)

View File

@ -27,6 +27,7 @@ struct some_alloc
{ {
typedef T value_type; typedef T value_type;
some_alloc(const some_alloc&); some_alloc(const some_alloc&);
void allocate(size_t);
}; };
int main(int, char**) int main(int, char**)

View File

@ -34,6 +34,7 @@ struct some_alloc
some_alloc() {} some_alloc() {}
some_alloc(const some_alloc&); some_alloc(const some_alloc&);
void allocate(size_t);
void deallocate(void*, unsigned) {} void deallocate(void*, unsigned) {}
typedef std::true_type propagate_on_container_swap; typedef std::true_type propagate_on_container_swap;
@ -46,6 +47,7 @@ struct some_alloc2
some_alloc2() {} some_alloc2() {}
some_alloc2(const some_alloc2&); some_alloc2(const some_alloc2&);
void allocate(size_t);
void deallocate(void*, unsigned) {} void deallocate(void*, unsigned) {}
typedef std::false_type propagate_on_container_swap; typedef std::false_type propagate_on_container_swap;

View File

@ -27,6 +27,7 @@ struct some_alloc
{ {
typedef T value_type; typedef T value_type;
some_alloc(const some_alloc&); some_alloc(const some_alloc&);
void allocate(size_t);
}; };
int main(int, char**) int main(int, char**)

View File

@ -25,6 +25,7 @@ struct some_alloc
typedef T value_type; typedef T value_type;
some_alloc(const some_alloc&); some_alloc(const some_alloc&);
~some_alloc() noexcept(false); ~some_alloc() noexcept(false);
void allocate(size_t);
}; };
int main(int, char**) int main(int, char**)

View File

@ -29,6 +29,7 @@ struct some_alloc
{ {
typedef T value_type; typedef T value_type;
some_alloc(const some_alloc&); some_alloc(const some_alloc&);
void allocate(size_t);
}; };
template <class T> template <class T>
@ -38,6 +39,7 @@ struct some_alloc2
some_alloc2() {} some_alloc2() {}
some_alloc2(const some_alloc2&); some_alloc2(const some_alloc2&);
void allocate(size_t);
void deallocate(void*, unsigned) {} void deallocate(void*, unsigned) {}
typedef std::false_type propagate_on_container_move_assignment; typedef std::false_type propagate_on_container_move_assignment;
@ -51,6 +53,7 @@ struct some_alloc3
some_alloc3() {} some_alloc3() {}
some_alloc3(const some_alloc3&); some_alloc3(const some_alloc3&);
void allocate(size_t);
void deallocate(void*, unsigned) {} void deallocate(void*, unsigned) {}
typedef std::false_type propagate_on_container_move_assignment; typedef std::false_type propagate_on_container_move_assignment;

View File

@ -27,6 +27,7 @@ struct some_alloc
{ {
typedef T value_type; typedef T value_type;
some_alloc(const some_alloc&); some_alloc(const some_alloc&);
void allocate(size_t);
}; };
int main(int, char**) int main(int, char**)

View File

@ -35,6 +35,7 @@ struct some_alloc
some_alloc() {} some_alloc() {}
some_alloc(const some_alloc&); some_alloc(const some_alloc&);
void allocate(size_t);
void deallocate(void*, unsigned) {} void deallocate(void*, unsigned) {}
typedef std::true_type propagate_on_container_swap; typedef std::true_type propagate_on_container_swap;
@ -47,6 +48,7 @@ struct some_alloc2
some_alloc2() {} some_alloc2() {}
some_alloc2(const some_alloc2&); some_alloc2(const some_alloc2&);
void allocate(size_t);
void deallocate(void*, unsigned) {} void deallocate(void*, unsigned) {}
typedef std::false_type propagate_on_container_swap; typedef std::false_type propagate_on_container_swap;