struct __allocator_traits_base
{
template<typename _Tp, typename _Up, typename = void>
- struct __rebind : __replace_first_arg<_Tp, _Up> { };
+ struct __rebind : __replace_first_arg<_Tp, _Up>
+ {
+ static_assert(is_same<
+ typename __replace_first_arg<_Tp, typename _Tp::value_type>::type,
+ _Tp>::value,
+ "allocator_traits<A>::rebind_alloc<A::value_type> must be A");
+ };
template<typename _Tp, typename _Up>
struct __rebind<_Tp, _Up,
__void_t<typename _Tp::template rebind<_Up>::other>>
- { using type = typename _Tp::template rebind<_Up>::other; };
+ {
+ using type = typename _Tp::template rebind<_Up>::other;
+
+ static_assert(is_same<
+ typename _Tp::template rebind<typename _Tp::value_type>::other,
+ _Tp>::value,
+ "allocator_traits<A>::rebind_alloc<A::value_type> must be A");
+ };
protected:
template<typename _Tp>
template<typename T, typename U>
using Rebind = typename std::allocator_traits<T>::template rebind_alloc<U>;
-#if __STDC_HOSTED__
-template<typename T>
+template<typename T, typename = T>
struct HasRebind {
using value_type = T;
- template<typename U> struct rebind { using other = std::allocator<U>; };
+ template<typename U> struct rebind { using other = HasRebind<U>; };
};
-static_assert(is_same<Rebind<HasRebind<int>, long>,
- std::allocator<long>>::value,
+// Would get HasRebind<long, int> here if the first template argument is
+// replaced instead of using the nested rebind.
+static_assert(is_same<Rebind<HasRebind<int>, long>, HasRebind<long>>::value,
"nested alias template is used");
-#endif
template<typename T>
struct NoRebind0 {
--- /dev/null
+// { dg-do compile { target c++11 } }
+#include <vector>
+
+// Custom allocator defined with std::allocator, but doesn't provide rebind.
+template<typename T> struct Alloc : std::allocator<T> { };
+
+std::vector<int, Alloc<int>> v; // { dg-error "here" "" { target c++17_down } }
+
+// Custom allocator that does provide rebind, but incorrectly.
+template<typename T> struct Alloc2
+{
+ using value_type = T;
+ template<typename U> struct rebind { using other = Alloc<U>; }; // not Alloc2
+ T* allocate(std::size_t n) { return std::allocator<T>().allocate(n); }
+ void deallocate(T* p, std::size_t n) { std::allocator<T>().deallocate(p, n); }
+};
+
+std::vector<int, Alloc2<int>> v2; // { dg-error "here" }
+
+// { dg-error "static assertion failed: .*rebind_alloc" "" { target *-*-* } 0 }
template<typename U>
nested_alloc(nested_alloc<U>) { }
+ // Need to customize rebind, otherwise nested_alloc<alloc<T>> gets rebound
+ // to nested_alloc<U>.
+ template<typename U>
+ struct rebind
+ {
+ using other = typename std::allocator_traits<A>::template rebind_alloc<U>;
+ };
+
A& outer_allocator() { return *this; }
template<typename U, typename... Args>
// { dg-error "non-const, non-volatile value_type" "" { target *-*-* } 0 }
// { dg-prune-output "std::allocator<.* has no member named " }
// { dg-prune-output "must have the same value_type as its allocator" }
+// { dg-prune-output "rebind_alloc" }
// { dg-prune-output "use of deleted function" }
// { dg-prune-output "must have the same value_type as its allocator" }
// { dg-prune-output "no match for call" }
+// { dg-prune-output "rebind_alloc" }
// { dg-prune-output "use of deleted function" }
// { dg-prune-output "must have the same value_type as its allocator" }
// { dg-prune-output "no match for call" }
+// { dg-prune-output "rebind_alloc" }
template<typename T>
struct A2 : std::allocator<T>
{
- template<typename U> struct rebind { typedef A1<U> other; };
+ template<typename U> struct rebind { typedef A2<U> other; };
A2() = default;
template<typename U> A2(const A2<U>&) { }