Uninitialized memory wrapper - c++

I'm looking for a class that is similar to std::optional, but without the internal flag which tells whether the container is empty or not. I want to be able to declare a variable of type T without invoking T' constructor, and later on move or emplace something into it on my discretion. Specifically I want to work with non-default-constructible T's.
It can be achieved easily with std::optional, but it comes with an overhead of the internal flag. I want this wrapper's size to be equal to sizeof(T).
I know such a class can be implemented using placement new (as are std::optional, std::variant etc). But it looks like a lot of work, and I'm wondering if something like that already exists...

There is nothing for it in the standard library, but it is relatively straight-forward to write such an unsafe optional as a union class. It still requires that you implement the constructor and methods with a placement-new (or construct_at).
However, such a class can't follow the RAII principle properly, because the destructor cannot assume that the unsafe optional is non-empty, so that it can't destroy the contained object. Instead the user of the unsafe optional has to manually choose to destruct the contained object before the unsafe optional's lifetime ends or before a new object is emplaced into it.
It would be preferably to rewrite the user code so that it isn't necessary to construct the empty unsafe optional first. The user code must know whether it contains an object anyway for the reason above, so it should always be possible. (I don't know your concrete use case, so I can't give concrete advice.)
From your comment it seems like you are writing a container. A container can use the standard Allocator concept together with std::allocator_traits as all the standard library allocator-aware containers (e.g. std::vector, std::map, etc.) do:
Your class takes a template parameter called A, usually defaulted to std::allocator<T> (the default allocator using operator new/operator delete), then define
using Alloc = typename std::allocator_traits<A>::template rebind<T>;
and store an instance alloc of Alloc as the allocator (possibly passed through a constructor or default-constructed).
Then to obtain memory you do
T* storage = std::allocator_traits<Alloc>::allocate(alloc, n);
where n is the number of elements to allocate memory for, without constructing any object.
Then to construct the i's object you do
std::allocator_traits<Alloc>::construct(alloc, &storage[i], /*constructor args*/);
To destruct the object you do
std::allocator_traits<Alloc>::destroy(alloc, &storage[i]);
and to deallocate the memory you do
std::allocator_traits<Alloc>::deallocate(alloc, n);
where n must be the same as the allocation size.
That way your container will automatically support all classes as allocator that follow the standard's Allocator concept and no dangerous casts or anything like that is required.

Related

Does the standard allow to rebind the allocator-type given with a

If you have a container and a given allocator is it mandated that the standard-libary always uses this allocator for T ? Consider a list<T>, where, when the given allocator is used, it would be necesssary to allocate the list node object and the T object separately, having a node object pointing to the T object. There's rebind_alloc to "cast" the allocator to another type so that you could specialize that allocator for internal types the container uses. Is this allowed or even used by the standard libraries ?
There's s similar question with an answer where someone said that he thinks that the container behave like that, but not with a definite reference.
Unless otherwise specified, all containers defined in this Clause obtain memory using an allocator [...] A copy of this allocator is used for any memory allocation and element construction performed, by these constructors and by all member functions
source.

Why are are std::allocator's construct and destroy functions deprecated in c++17?

The c++17 specification deprecates the construct and destroy members of the std::allocator object. The working group provided rationale for deprecating other member functions here, under the heading "Deprecate the redundant members of std::allocator".
However they don't mention specifically why those two members are deprecated or what the recommendation is for replacing that functionality. I'm assuming the implication is to use std::allocator_traits::construct instead.
I'm a bit confused about whether implementing construct may actually still be necessary in some cases though because of this comment about std::allocator_traits::construct
Because this function provides the automatic fall back to placement new, the member function construct() is an optional Allocator requirement since C++11.
For custom allocators (e.g. for page-aligned memory using memalign), will falling back to placement new always produce the correct behavior?
The allocator requirements table says that construct(c, args), if provided, must "construct an object of type C at c".
It says absolutely nothing about 1) what arguments are to be passed to C's constructor or 2) how these arguments are to be passed. That's the allocator's choice, and in fact two allocators in the standard do mess with the arguments before passing them to C's constructor: std::scoped_allocator_adaptor and std::pmr::polymorphic_allocator. When constructing a std::pair, in particular, the arguments they pass to pair's constructor may not even resemble the ones they received.
There's no requirement to perfectly forward, either; a C++03-style construct(T*, const T&) is conforming if inefficient.
std::allocator's construct and destroy are deprecated because they are useless: no good C++11 and later code should ever call them directly, and they add nothing over the default.
Handling memory alignment should be the task of allocate, not construct.
The functions were removed along with others from the paper D0174R0 Deprecating Vestigial Library Parts in C++17. If we look at the relevant section we have
Many members of std::allocator redundantly duplicate behavior that is otherwise produced by std::allocator_traits<allocator<T>>, and could safely be removed to simplify this class. Further, addressof as a free function supersedes std::allocator<T>::address which requires an allocator object of the right type. Finally, the reference type aliases were initially provided as an expected means for extension with other allocators, but turned out to not serve a useful purpose when we specified the allocator requirements (17.6.3.5 [allocator.requirements]).
While we cannot remove these members without breaking backwards compatibility with code that explicitly used this allocator type, we should not be recommending their continued use. If a type wants to support generic allocators, it should access the allocator's functionality through allocator_traits rather than directly accessing the allocator's members - otherwise it will not properly support allocators that rely on the traits to synthesize the default behaviors. Similarly, if a user does not intend to support generic allocators, then it is much simpler to directly invoke new, delete, and assume the other properties of std::allocator such as pointer-types directly.
Emphasis mine
So the rational was we do not need to duplicate all of the code in allocator since we have the allocator traits. If we look at std::allocator_traits we will see that it does have
allocate
deallocate
construct
destroy
max_size
static functions so we can use those instead of the ones in the allocator.

Why is an allocator allowed to override the ctors and dtors of its value_type?

As far as I know, containers are supposed to use std::allocator_traits<Alloc>::construct and std::allocator_traits<Alloc>::destroy which are either Alloc::construct and Alloc::destroy respectively or value_type::value_type and value_type::~value_type (the defaults).
Why allow an allocator to override the default ctor and dtor of its value_type? When is it useful?
This allows the allocator to construct objects in custom ways.
An example based on standard library features is the std::scoped_allocator_adaptor which will pass an allocator to any objects it constructs (as long as they support construction from allocators). That is useful when you want all elements of a container to use the same allocator as the container itself. When the container's allocator constructs an element it passes itself (or a copy of itself rebound to the element type) to the element.
Another example is if you have huge vectors of a trivial type like double but you don't want to pay the cost of zero-initializing all the vector elements if you're going to re-assign them new values anyway. You can have a custom allocator that does nothing in its construct function, so that the elements are left uninitialized when the vector is resized.

How I can use emplace for unordered_set that holds shared_ptr to the object?

Say I have an object:
struct Foo {
Foo(const std::string& str1, const std::string& str1)
: mStr1(str1), mStr2(str2)
{}
std::string mStr1;
std::string mStr2;
};
And set
typedef std::unordered_set<std::shared_ptr<Foo> , Hash, Compare> Set;
I have custom hasher and compare. But when I say:
Set set;
set.emplace(str1, str2);
I receive compile error, because the constructor of Foo is obviously not a constructor of std::shared_ptr<Foo>. What I would like is when emplace needs to construct a pointer to use std::make_shared<Foo>(str1, str2)
It seems that I also need a custom allocator for that, but I did not manage to implement one that satisfy the compiler.
My question is: Is what I want possible. If yes, how - is the allocator the right way to go. If yes, can you point me to an example.
Use set.insert(std::make_shared<Foo>(str1, str2));. emplace is generally not a gain for container with unique keys when duplicate keys are an issue, because of the way it operates:
It must construct the object first before it can compare it to existing keys in the container to determine if it can be inserted.
Once the object is constructed, it cannot be copied or moved, because the object is not required to be copyable or movable. Moreover, there's no reliable way for emplace to detect when it can copy or move something, because there are plenty of types for which is_copy_constructible returns true but cannot be actually copied.
Object construction can only happen once, since the constructor may move from the arguments or have other side effects.
A typical implementation of emplace thus always allocates memory for the node up-front, constructs the object inside that memory, compares it with existing elements in the container, and then either links it in, or destroys it and deallocates the memory.
insert, on the other hand, has the key readily available. It can therefore first decide whether the node should be inserted, and only allocate memory if it should be.
In theory, implementations might special-case emplace for the "one argument with the same type as the element type" case. But I know of no implementation that actually does this.
You can just use std::make_shared directly in the argument list of emplace.
set.emplace(std::make_shared<Foo>(str1, str2));
No custom allocator required.
I receive compile error, because the constructor of Foo is obviously
not a constructor of std::shared_ptr. What I would like is when
emplace needs to construct a pointer to use
std::make_shared(str1, str2)
emplace is implemented as a function that uses perfect forwarding to invoke the constructor of the contained element (in this case shared_ptr). The contained element's constructor accepts a pointer to Foo, therefore you should be able to do this (just like you would construct a shared_ptr<Foo> object):
set.emplace(new Foo("x", "y")); //or
set.emplace(new Foo(str1, str2));
It seems that I also need a custom allocator for that, but I did not
manage to implement one that satisfy the compiler.
A custom allocator is a total overkill if all you want to do is add a shared_ptr in the most efficient way (by invoking forwarding constructor on some pre-allocate element), or I'm totally misunderstanding your question. You would typically use the allocator if you don't want the element to be constructed using the default allocator (which use operator new). In this case, shared_ptr itself will be the element that will be constructed on the heap. You would only use an allocator if you are concerned that heap allocations are for some reason inefficient for your purposes (e.g if you allocate millions of small objects).
Note (as commented by #Yakk) that, in this case it is possible that the instantiation of shared_ptr may throw (I can only think of bad_alloc as possibility), in which case the pointer passed to emplace would cause a leak. For this reason I too think std::make_shared would be a better option (as mentioned in another answer).

C++:using pointer to unordered_map or just defining it as a member variable from this type in a class?

I have a problem which I cannot understand:
Let's Say I have a class System with several member fields, and one of them is of type unordered_map, so when I declare the class in the header file, I write at the beginning of the header #include <unordered_map>.
Now, I have two ways of declaring this field:
1.std::unordered_map<std::string,int> umap;
2.std::unordered_map<std::string,int>* p_umap;
Now in the constructor of the class, if I choose the first option, there is no need to initialize that field in the initializer list since the constructor of class System will call the default constructor for the field umap as part of constructing an instance of type class System.
If I choose the second option, I should initialize the field p_umap in the constructor (in the initialize list) with the operator new and in the destructor, to delete this dynamic allocation.
What is the difference between these two options? If you have a class that one of it's fields is of type unordered_map, how do you declare this field? As a pointer or as a variable of type unordered_map?
In a situation like the one you are describing, it seems like the first option is preferable. Most likely, in fact, the unordered map is intended to be owned by the class it is a data member of. In other words, its lifetime should not be extended beyond the lifetime of the encapsulating class, and the encapsulating class has the responsibility of creating and destroying the unordered map.
While with option 1 all this work is done automatically, with option 2 you would have to take care of it manually (and take care of correct copy-construction, copy-assignment, exception-safety, lack of memory leaks, and so on). Surely you could use smart pointers (e.g. std::unique_ptr<>) to encapsulate this responsibility into a wrapper that would take care of deleting the wrapped object when the smart pointer itself goes out of scope (this idiom is called RAII, which is an acronym for Resource Acquisition Is Initialization).
However, it seems to me like you do not really need a pointer at all here. You have an object whose lifetime is completely bounded by the lifetime of the class that contains it. In these situations, you should just not use pointers and prefer declaring the variable as:
std::unordered_map<std::string, int> umap;
Make it not a pointer until you need to make it a pointer.
Pointers are rife with user error.
For example, you forgot to mention that your class System would also need to implement
System( const Sysytem& )
and
System& operator= ( const System& )
or Bad Behavior will arise when you try to copy your object.
The difference is in how you want to be able to access umap. Pointers can allow for a bit more flexibility, but they obviously add complexity in terms of allocation (stack vs heap, destructors and such). If you use a pointer to umap, you can do some pretty convoluted stuff such as making two System's with the same umap. In the end though, go with KISS unless there's a compelling reason not to.
There is no need to define it as pointer. If you do it, you must also make sure to implement copy constructor and assignment operator, or disable them completely.
If there is no specific reason to make it a pointer (and you don't show any) just make it a normal member variable.