Deprecation of std::allocator<void>
Asked Answered
P

2

19

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).

Pinard answered 9/5, 2018 at 2:42 Comment(3)
groups.google.com/a/isocpp.org/forum/#!topic/std-discussion/… there's a discussion here.Pallor
Trying to ping @NicolBolas who participated in the discussion, the question is rather interesting to me as well.Argos
Related: What is a void std::allocator? ie: std::allocator<void>.T
U
5

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 both allocate and deallocate member functions, which cannot be synthesized by default from allocator_traits. That need went away with C++11 and the void_pointer and const_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.

Undecagon answered 9/5, 2018 at 7:51 Comment(0)
F
5

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);
}
Fatuity answered 8/1, 2019 at 13:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.