[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>
struct __rebind_alloc_helper {
#ifndef _LIBCPP_CXX03_LANG

View File

@ -1266,7 +1266,7 @@ public:
typedef typename __base::const_reference const_reference;
typedef typename __base::iterator 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::pointer pointer;
@ -1289,7 +1289,14 @@ public:
explicit deque(size_type __n, const _Allocator& __a);
#endif
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>
deque(_InputIter __f, _InputIter __l,
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);
}
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 _InputIter>
deque<_Tp, _Allocator>::deque(_InputIter __f, _InputIter __l,

View File

@ -186,6 +186,7 @@ template <class T, class Allocator, class Predicate>
#include <iterator>
#include <limits>
#include <memory>
#include <type_traits>
#include <version>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@ -647,7 +648,7 @@ public:
typedef const value_type& const_reference;
typedef typename allocator_traits<allocator_type>::pointer 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 base::iterator iterator;
@ -669,7 +670,13 @@ public:
explicit forward_list(size_type __n, const allocator_type& __a);
#endif
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>
forward_list(_InputIterator __f, _InputIterator __l,
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);
}
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 _InputIterator>
forward_list<_Tp, _Alloc>::forward_list(_InputIterator __f, _InputIterator __l,

View File

@ -853,7 +853,7 @@ public:
typedef const value_type& const_reference;
typedef typename base::pointer 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::iterator iterator;
typedef typename base::const_iterator const_iterator;
@ -885,7 +885,16 @@ public:
explicit list(size_type __n, const allocator_type& __a);
#endif
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>
list(_InpIter __f, _InpIter __l,
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);
}
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 _InpIter>
list<_Tp, _Alloc>::list(_InpIter __f, _InpIter __l,

View File

@ -359,7 +359,7 @@ public:
typedef allocator_traits<allocator_type> __alloc_traits;
typedef value_type& 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::pointer pointer;
typedef typename __alloc_traits::const_pointer const_pointer;
@ -395,7 +395,21 @@ public:
explicit vector(size_type __n, const allocator_type& __a);
#endif
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>
vector(_InputIterator __first,
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 _InputIterator>
vector<_Tp, _Allocator>::vector(_InputIterator __first,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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