Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deprecation of std::allocator<void>

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

like image 769
Lingxi Avatar asked May 09 '18 02:05

Lingxi


People also ask

Is the allocator<void> deprecated?

It's not that std::allocator<void> is deprecated, just it isn't a explicit specialisation.

Do we need to specialise the allocator for voids?

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.

Can deprecated features be removed from a program?

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

Is NOT1 deprecated in C++ 17?

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


2 Answers

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);
}
like image 111
Artyer Avatar answered Sep 19 '22 19:09

Artyer


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.

like image 42
sp2danny Avatar answered Sep 19 '22 19:09

sp2danny