Related: Why do standard containers require allocator_type::value_type to be the element type?
It is said that the following has been deprecated since C++17:
template<>
struct allocator<void>;
I wonder whether it is deprecated because the primary template alone is now able to accommodate allocator<void>
, or the use-case of allocator<void>
is deprecated.
If latter, I wonder why. I think allocator<void>
is useful in specifying an allocator not bound to a specific type (so just some schema/metadata).
It's not that std::allocator<void> is deprecated, just it isn't a explicit specialisation.
Now, since std::allocator<T>::reference and std::allocator<T>::const_reference have been deprecated, there doesn't need to be an explicit specialisation for void.
(Note that implementations are allowed to warn about anything, but they can definitely warn about usage of deprecated features. The Standard now has an attribute for this very purpose, for marking code to emit such warnings.) In a following Standard, deprecated features can be removed outright.
error C4996: ‘std::not1’: warning STL4008: std::not1 (), std::not2 (), std::unary_negate, and std::binary_negate are deprecated in C++17. They are superseded by std::not_fn ().
It's not that std::allocator<void>
is deprecated, just it isn't a explicit specialisation.
What it used to look like was something like:
template<class T>
struct allocator {
typedef T value_type;
typedef T* pointer;
typedef const T* const_pointer;
// These would be an error if T is void, as you can't have a void reference
typedef T& reference;
typedef const T& const_reference;
template<class U>
struct rebind {
typedef allocator<U> other;
}
// Along with other stuff, like size_type, difference_type, allocate, deallocate, etc.
}
template<>
struct allocator<void> {
typedef void value_type;
typedef void* pointer;
typedef const void* const_pointer;
template<class U>
struct rebind {
typdef allocator<U> other;
}
// That's it. Nothing else.
// No error for having a void&, since there is no void&.
}
Now, since std::allocator<T>::reference
and std::allocator<T>::const_reference
have been deprecated, there doesn't need to be an explicit specialisation for void
. You can just use std::allocator<void>
, along with std::allocator_traits<std::allocator<void>>::template rebind<U>
to get std::allocator<U>
, you just can't instantiate std::allocator<void>::allocates
.
For example:
template<class Alloc = std::allocator<void>>
class my_class; // allowed
int main() {
using void_allocator = std::allocator<void>;
using void_allocator_traits = std::allocator_traits<void_allocator>;
using char_allocator = void_allocator_traits::template rebind_alloc<char>;
static_assert(std::is_same<char_allocator, std::allocator<char>>::value, "Always works");
// This is allowed
void_allocator alloc;
// These are not. Taking the address of the function or calling it
// implicitly instantiates it, which means that sizeof(void) has
// to be evaluated, which is undefined.
void* (void_allocator::* allocate_mfun)(std::size_t) = &void_allocator::allocate;
void_allocator_traits::allocate(alloc, 1); // calls:
alloc.allocate(1);
}
According to p0174r0
Similarly,
std::allocator<void>
is defined so that various template rebinding tricks could work in the original C++98 library, but it is not an actual allocator, as it lacks bothallocate
anddeallocate
member functions, which cannot be synthesized by default fromallocator_traits
. That need went away with C++11 and thevoid_pointer
andconst_void_pointer
type aliases in allocator_traits. However, we continue to specify it in order to avoid breaking old code that has not yet been upgraded to support generic allocators, per C++11.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With