why does allocator in c++ need a copy constructor?
Asked Answered
G

4

12

It is said here that it's because of exception specification. I do not understand it. Does this question have any relationship with exception specification?

Giannagianni answered 21/8, 2011 at 8:31 Comment(0)
N
10

After reading through the tutorial I was a little confused myself by the wording. But I believe it's as simple as this: the tutorial was explaining why the allocator's template header shows

allocator(const allocator&) throw();

and

template <class U> allocator(const allocator<U>&) throw();

even though the copy constructor is fairly useless for an allocator. And the answer was that the specification of an allocator does not allow the constructor to throw exceptions. Therefore the copy constructor public interface defines copy constructors with an exception specification of throw() (does not throw any exceptions) to prevent someone deriving their own allocator with copy constructors which might throw an exception.

See this link for a good description of what an exception specification is if that's what was throwing you. (No pun intended. Really.)

So, they didn't mean that when creating an allocator, you have to provide a copy constructor. They just were pointing out that the specification specifically prohibits you from defining one that throws any exceptions. `

Natika answered 21/8, 2011 at 9:1 Comment(3)
You are mistaken, I'm afraid. The copy constructor is absolutely required and is anything but useless. It is not allowed to throw, but it is not required to have a noexcept or throw() specification (though it is is a good idea to add that. The reason that allocators must be copy constructible is that the container classes store a copy of the allocator. (BTW, I wrote about 2/3 of the allocator text in the standard, starting with C++11, but allocators were required to be copy constructible from the very beginning.)Earlearla
BTW, Starting with C++11, allocators are not required to be copy-assignable, but if they are, then the assignment operator must also not throw.Earlearla
Hi Pablo. I agree given what you describe that it doesn't sound useless! (I did qualify "useless" so I'd like to think I meant something nuanced, but I honestly have no idea at this point.) Unfortunately, I remember nothing about this post including whatever led me to say that. I thought about trying to fix the answer, but it's been a long time since I used C++, the link given by OP is broken, my link is broken (didn't archive it). and your explanation/answer is better than anything I could do. So I'm going to recommend anyone finding themselves here go check out your post instead. Cheers! :)Natika
L
3

You have to explicitly write a copy constructor (rather than use the default) because the copy constructor for a C++03 allocator is required to be defined with the exception specifier throw(). The default copy constructor does not have this specifier.

Technically, you don't have to, but if it does throw an exception... well, good luck with that.

But that's just a minor annoyance, since allocators in C++03 cannot have state. So you shouldn't be copying members around. The copy constructor can be empty.

Leal answered 21/8, 2011 at 8:53 Comment(3)
Nicol Bolas,will it be OK if allocator(const allocator&)=delete;? as we are in 2016Retene
@Explorer_N No (at least in the sense of the standard Allocator). The standard requires it to be copy constructible.Aharon
This is not quite right. The copy constructor is required not to throw, but is not required to have a nothrow specifier (throw() or noexcept). The compiler-generated copy constructor is still a copy constructor and is just fine for many allocator types. The std::allocator template has a copy constructor explicitly specified as having a noexcept specifier, just because it helps the compiler a little (though not much, honestly -- it's mostly historical).Earlearla
P
2

The allocator requires a copy constructor because containers have a copy constructor and will need to copy their allocator in the process.

Pictish answered 21/8, 2011 at 8:50 Comment(1)
Prior to C++11, allocators were necessarily stateless. Consequently calling to the default constructor would suffice. Post C++11, whether allocators are copied by their enclosing containers under a given circumstance is determined by the presence and definition of the allocators select_on_container_X_Y methods. See the allocator traits referenceDharma
E
2

It's actually pretty simple. The constructor of a container using an allocator takes the allocator and stores a copy of it. In order to do that, it needs the allocator to be CopyConstructible. That's all. Note that an allocator type not required to CopyAssignable unless its propagate_on_container_copy_assignment trait is true (which is rare).

The C++11 specification also states that "No constructor, comparison operator, copy operation, move operation, or swap operation on these types shall exit via an exception." The exception rules allow you to make a (stack) copy of an allocator (especially during construction or destruction) without worrying that copying the allocator will throw. Designing containers that are exception-safe in the presence of allocators that might throw on copy, move, swap, or compare is nearly impossible. In practice, an allocator cannot hold much more than a pointer to some resource, so allowing allocators to throw on copy, etc., would add a lot of pain for virtually no gain.

Earlearla answered 2/9, 2017 at 20:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.