std::optional implemented as union vs char[]/aligned_storage - c++

While reading through GCC's implementation of std::optional I noticed something interesting. I know boost::optional is implemented as follows:
template <typename T>
class optional {
// ...
private:
bool has_value_;
aligned_storage<T, /* ... */> storage_;
}
But then both libstdc++ and libc++ (and Abseil) implement their optional types like this:
template <typename T>
class optional {
// ...
private:
struct empty_byte {};
union {
empty_byte empty_;
T value_;
};
bool has_value_;
}
They look to me as they are functionally identical, but are there any advantages of using one over the other? (Except for the obvious lack of placement new in the latter which is really nice.)

They look to me as they are functionally identical, but are there any advantages of using one over the other? (Except for the obvious lack placement new in the latter which is really nice.)
It's not just "really nice" - it's critical for a really important bit of functionality, namely:
constexpr std::optional<int> o(42);
There are several things you cannot do in a constant expression, and those include new and reinterpret_cast. If you implemented optional with aligned_storage, you would need to use the new to create the object and reinterpret_cast to get it back out, which would prevent optional from being constexpr friendly.
With the union implementation, you don't have this problem, so you can use optional in constexpr programming (even before the fix for trivial copyability that Nicol is talking about, optional was already required to be usable as constexpr).

std::optional cannot be implemented as aligned storage, due to a post-C++17 defect fix. Specifically, std::optional<T> is required to be trivially copyable if T is trivially copyable. A union{empty; T t}; will satisfy this requirement
Internal storage and placement-new/delete usage cannot. Doing a byte copy from a TriviallyCopyable object to storage that does not yet contain an object is not sufficient in the C++ memory model to actually create that object. By contrast, the compiler-generated copy of an engaged union over TriviallyCopyable types will be trivial and will work to create the destination object.
So std::optional must be implemented this way.

Related

What is the need for MaxAlign trick when creating static singleton?

Following a snippet of code from Loki singleton implementation which shows what it calls "MaxAlign Trick". I assume it has something to do with alignment (duh!), but what's the purpose of trying to align with all the types mentioned inside the union? Will the placement new inside Create() break without it?
template <class T> struct CreateStatic
{
union MaxAlign
{
char t_[sizeof(T)];
short int shortInt_;
int int_;
long int longInt_;
float float_;
double double_;
long double longDouble_;
struct Test;
int Test::* pMember_;
int (Test::*pMemberFn_)(int);
};
static T* Create()
{
static MaxAlign staticMemory_;
return new(&staticMemory_) T;
}
// other code...
}
MaxAlign serves two purposes. First, it is an implementation of the C++11 std::max_align_t: "a trivial standard-layout type whose alignment requirement is at least as strict (as large) as that of every scalar type." (cppreference). Since the alignment of a type is the alignment of the data member with the highest alignment requirements, the definition of MaxAlign gives us exactly that: a type that is guaranteed to have the max. alignment for the platform of interest.
Second, it is also a buffer that is large enough to contain a T:
char t_[sizeof(T)];
Taking both aspects, MaxAlign provides the C++11 feature std::aligned_storage_t<size, alignment> (without taking over-alignment of T is into account - it probably didn't even exist back then).
But why is it needed: placement new requires the buffer to be suitably aligned for the instance that is being constructed. Without this "alignment trick", you might end up with undefined behaviour. T being an unkonwn type, Loki circumvents any risks by choosing the maximal alignment for the platform the code is being compiled for.
In modern code, you would probably not use placement new, but use a static object on the stack, e.g.
static T& Create() {
static T instance;
return instance;
}
But 20 years ago, this might not have worked properly across compilers and/or in multi-threaded environments (proper initialisation of T instance in the above is only guaranteed since C++11 IIRC).

Templated member variables in C++

Often when writing templated code, I find myself needing to store an instance of the template type in a member variable. For example, I might need to cache a value to be used later on. I would like to be able to write my code as:
struct Foo
{
template<typename T>
T member;
template<typename T>
void setMember(T value)
{
member<T> = value;
}
template<typename T>
T getMember()
{
return member<T>;
}
};
Where members are specialized as they are used. My question:
Is such templated member variable possible with current C++ generative coding facilities?
If not, are there any proposals for such a language feature?
If not, are there any technical reasons why such a thing is not possible?
It should be obvious that I do not want to list all possible types (e.g. in a std::variant) as that is not generative programming and would not be possible if the user of the library is not the same as the author.
Edit: I think this somewhat answers my 3rd question from above. The reason being that today's compilers are not able to postpone instantiation of objects to after the whole program has been parsed:
https://stackoverflow.com/a/27709454/3847255
This is possible in the library by combining existing facilities.
The simplest implementation would be
std::unordered_map<std::type_index, std::any>
This is mildly inefficient since it stores each std::type_index object twice (once in the key and once inside each std::any), so a std::unordered_set<std::any> with custom transparent hash and comparator would be more efficient; this would be more work though.
Example.
As you say, the user of the library may not be the same as the author; in particular, the destructor of Foo does not know which types were set, but it must locate those objects and call their destructors, noting that the set of types used may be different between instances of Foo, so this information must be stored in a runtime container within Foo.
If you're wary about the RTTI overhead implied by std::type_index and std::any, we can replace them with lower-level equivalents. For std::type_index you can use a pointer to a static tag variable template instantiation (or any similar facility), and for std::any you can use a type-erased std::unique_ptr<void, void(*)(void*)> where the deleter is a function pointer:
using ErasedPtr = std::unique_ptr<void, void(*)(void*)>;
std::unordered_map<void*, ErasedPtr> member;
struct tag {};
template<class T> inline static tag type_tag;
member.insert_or_assign(&type_tag<T>, ErasedPtr{new T(value), [](void* p) {
delete static_cast<T*>(p);
}});
Example. Note that once you make the deleter of std::unique_ptr a function pointer, it is no longer default-constructible, so we can't use operator[] any more but must use insert_or_assign and find. (Again, we've got the same DRY violation / inefficiency, since the deleter could be used as the key into the map; exploiting this is left as an exercise for the reader.)
Is such templated member variable possible with current C++ generative coding facilities?
No, not exactly what you describe. What is possible is to make the enclosing class a template and use the template parameters to describe the types of the class' members.
template< typename T >
struct Foo
{
T member;
void setMember(T value)
{
member = value;
}
T getMember()
{
return member;
}
};
In C++14 and later, there are variable templates, but you can't make a template non-static data member of a class.
If not, are there any proposals for such a language feature?
Not that I know of.
If not, are there any technical reasons why such a thing is not possible?
The primary reason is that that would make it impossible to define binary representation of the class. As opposed to templates, a class is a type, which means its representation must be fixed, meaning that at any place in the program Foo and Foo::member must mean the same things - same types, same object sizes and binary layout, and so on. A template, on the other hand, is not a type (or, in case of variable templates, is not an object). It becomes one when it is instantiated, and each template instantiation is a separate type (in case of variable templates - object).

Why are mutexes and condition variables trivially copyable?

LWG 2424 discusses the undesirable status of atomics, mutexes and condition variables as trivially copyable in C++14. I appreciate that a fix is already lined up, but std::mutex, std::condition variable et al. appear to have non-trivial destructors. For example:
30.4.1.2.1 Class mutex [thread.mutex.class]
namespace std {
class mutex {
public:
constexpr mutex() noexcept;
~mutex(); // user-provided => non-trivial
…
}
}
Shouldn't this disqualify them as trivially copyable?
Either it was my mistake, or I was misquoted, and I honestly don't recall which.
However, I have this very strongly held advice on the subject:
Do not use is_trivial nor is_trivially_copyable! EVER!!!
Instead use one of these:
is_trivially_destructible<T>
is_trivially_default_constructible<T>
is_trivially_copy_constructible<T>
is_trivially_copy_assignable<T>
is_trivially_move_constructible<T>
is_trivially_move_assignable<T>
Rationale:
tldr: See this excellent question and correct answer.
No one (including myself) can remember the definition of is_trivial and is_trivially_copyable. And if you do happen to look it up, and then spend 10 minutes analyzing it, it may or may not do what you intuitively think it does. And if you manage to analyze it correctly, the CWG may well change its definition with little or no notice and invalidate your code.
Using is_trivial and is_trivially_copyable is playing with fire.
However these:
is_trivially_destructible<T>
is_trivially_default_constructible<T>
is_trivially_copy_constructible<T>
is_trivially_copy_assignable<T>
is_trivially_move_constructible<T>
is_trivially_move_assignable<T>
do exactly what they sound like they do, and are not likely to ever have their definition changed. It may seem overly verbose to have to deal with each of the special members individually. But it will pay off in the stability/reliability of your code. And if you must, package these individual traits up into a custom trait.
Update
For example, clang & gcc compile this program:
#include <type_traits>
template <class T>
void
test()
{
using namespace std;
static_assert(!is_trivial<T>{}, "");
static_assert( is_trivially_copyable<T>{}, "");
static_assert( is_trivially_destructible<T>{}, "");
static_assert( is_destructible<T>{}, "");
static_assert(!is_trivially_default_constructible<T>{}, "");
static_assert(!is_trivially_copy_constructible<T>{}, "");
static_assert( is_trivially_copy_assignable<T>{}, "");
static_assert(!is_trivially_move_constructible<T>{}, "");
static_assert( is_trivially_move_assignable<T>{}, "");
}
struct X
{
X(const X&) = delete;
};
int
main()
{
test<X>();
}
Note that X is trivially copyable, but not trivially copy constructible. To the best of my knowledge, this is conforming behavior.
VS-2015 currently says that X is neither trivially copyable nor trivially copy constructible. I believe this is wrong according to the current spec, but it sure matches what my common sense tells me.
If I needed to memcpy to uninitialized memory, I would trust is_trivially_copy_constructible over is_trivially_copyable to assure me that such an operation would be ok. If I wanted to memcpy to initialized memory, I would check is_trivially_copy_assignable.
Not all implementations provide a nontrivial destructor for mutex. See libstdc++ (and assume that __GTHREAD_MUTEX_INIT has been defined):
// Common base class for std::mutex and std::timed_mutex
class __mutex_base
{
// […]
#ifdef __GTHREAD_MUTEX_INIT
__native_type _M_mutex = __GTHREAD_MUTEX_INIT;
constexpr __mutex_base() noexcept = default;
#else
// […]
~__mutex_base() noexcept { __gthread_mutex_destroy(&_M_mutex); }
#endif
// […]
};
/// The standard mutex type.
class mutex : private __mutex_base
{
// […]
mutex() noexcept = default;
~mutex() = default;
mutex(const mutex&) = delete;
mutex& operator=(const mutex&) = delete;
};
This implementation of mutex is both standard conforming and trivially copyable (which can be verified via Coliru). Similarly, nothing stops an implementation from keeping condition_variable trivially destructible (cf. [thread.condition.condvar]/6, although I couldn't find an implementation that does).
The bottom line is that we need clear, normative guarantees, and not clever, subtle interpretations of what condition_variable does or doesn't have to do (and how it has to accomplish that).
It's important to look at this from a language lawyer perspective.
It's basically impossible for an implementation to implement mutex, condition variables, and the like in a way that leaves them trivially copyable. At some point, you have to write a destructor, and that destructor is very likely going to have to do non-trivial work.
But that doesn't matter. Why? Because the standard does not explicitly state that such types will not be trivially copyable. And therefore it is, from the perspective of the standard, theoretically possible for such objects to be trivially copyable.
Even though no functional implementation every can be, the point of N4460 is to make it very clear that such types will never be trivially copyable.

Upcasting a const vector

Say I have
class Parent
{};
class ChildOne : Parent
{};
class ChildTwo : Parent
{};
void SomeMethod(std::vector<Parent*>& parents);
I understand why I can't pass a std::vector<ChildOne*> as an argument to SomeMethod as it's possible that SomeMethod may be:
void SomeMethod(std::vector<Parent*>& parents)
{
parents.push_back(new ChildTwo);
}
which would be invalid.
But why can't I pass a std::vector<ChildOne*> as an argument to
void AnotherMethod(const std::vector<Parent*>& parents);
In this case I can't think of what could go wrong, but I must be missing something.
EDIT:
To clarify, I'm wondering why this restriction exists. In C# (for example), this would compile (with IEnumerables). I assume that there is a failure case that exists.
You're right that it could work in this particular case and on most common platforms. However, it would be a terrible can of worms to allow this generally.
Consider that the standard doesn't guarantee all data pointers to be of the same size (for example, there are platforms where char* is larger than int*, because they don't have individually addressible bytes). The fact that you can convert a pointer-to-derived into a pointer-to-base does not mean that this conversion is trivial and does not affect the representation in memory. After all, as soon as multiple inheritance enters the picture, even this conversion start to involve changing the value of the pointer.
At the same time, templates are quite opaque in general. We know that a sane implementation of std::vector<T*> will use the same layout for its data for all Ts, as long as T* are the same size. But in general, a template can do anything based on its template arguments, with totally incompatible data layout.
Not to mention specialisation - if the template was specialised for <ChildOne*>, it could be totally incompatible.
Yes, there are cases where a const reference to a particular implementation of a template instantiated with Parent* could be aliased to the same template instantiated with ChildOne*. But these are extremely fragile, and in the general case such substitutability would allow innumerable bugs.
If you know that on your platform with your implementation of the standard library and with your types it's safe, you can create a casting function for it:
template <class D, class S>
const std::vector<D*>& base_cast(const std::vector<S*> &src)
{
return reinterpret_cast<const std::vector<D*>&>(src);
}
But use at your own risk.
std::vector<ChildOne*>
and
std::vector<Parent*>
are two different/"unrelated" types.
Years later, but I found a section of "The c++ Programming Language" that answers this question, section 27.2.1
"Logically, we count treat an immutable set<Circle*> as an immutable set because the problem of inserting an inappropriate element into the set cannot occur when we cannot change the set. That is, we could provide a conversion from const set<const Circle*> to const set<const Shape*>. The language doesn't do so by default, but the designer of set could."

Writing containers that can handle implicit sharing, but turn it off for non-copyable types (like unique_ptr)?

I dug up an old Grid class, which is just a simple 2-D container templated with a type. To make one you would do this:
Grid<SomeType> myGrid (QSize (width, height));
I tried to make it "Qt-ish"...for instance it does size operations in terms of QSize, and you index into it with myGrid[QPoint (x, y)]. It can take boolean masks and do operations on elements whose mask bit was set. There's also a specialization where if your elements are QColor it can generate a QImage for you.
But one major Qt idiom I adopted was that it did implicit sharing under the hood. This turned out to be very useful in the QColor-based grids for the Thinker-Qt-based program I had.
However :-/ I also happened to have some cases where I'd written the likes of:
Grid< auto_ptr<SomeType> > myAutoPtrGrid (QSize (width, height));
When I moved up from auto_ptr to C++11's unique_ptr, the compiler rightfully complained. Implicit sharing requires the ability to make an identical copy if needed...and auto_ptr had swept this bug under the rug by conflating copying with transfer-of-ownership. Non-copyable types and implicit sharing simply do not mix, and unique_ptr is kind enough to tell us.
(Note: It so happened that I hadn't noticed the problem in practice, because the use cases for the auto_ptr were passing grids by reference...never by value. Still, this was bad code...and the proactive nature of C++11 is pointing out the potential problem before it happens.)
Ok, so...how might I design a generic container that can flip implicit sharing on and off? I really did want many of the Grid features when I was using the auto_ptr and it's great if copying is disabled for non-copyable types...that catches errors! But having the implicit sharing work is nice as a default, when the type happens to be copyable.
Some ideas:
I could make separate types (NonCopyableGrid, CopyableGrid)...or (UniqueGrid, Grid) depending on your tastes...
I could pass a flag into the Grid constructor
I could use static factory methods (Grid::newNonCopyable, Grid::newCopyable) but which would call the relevant constructor under the hood...maybe more descriptive
If possible, I might "detect" copyability on the contained type, and then either leverage a QSharedDataPointer in the implementation or not, depending?
Any good reasons to pick one of these methods over the others, or have people adopted something altogether better for this kind of situation?
If you were going to do it in a single container, I think the easiest way would be to use std::is_copy_constructable to choose whether your data struct inherited from QSharedData, and to replace QSharedDataPointer with std::unique_ptr (QScopedPointer doesn't support move semantics)
This is only a rough example of what I'm thinking as I don't have Qt and C++11 available together:
template<class T>
class Grid
{
struct EmptyStruct
{
};
typedef typename std::conditional<
std::is_copy_constructible<T>::value,
QSharedData,
EmptyStruct
>::type GridDataBase;
struct GridData : public GridDataBase
{
// data goes here
};
typedef typename std::conditional<
std::is_copy_constructible<T>::value,
QSharedDataPointer<GridData>,
std::unique_ptr<GridData>
>::type GridDataPointer;
public:
Grid() : data_(new GridData) {}
private:
GridDataPointer data_;
};
Disclaimer
I don't really understand your Grid template or your use cases. However I do understand containers in general. So maybe this answer applies to your Grid<T> and maybe it doesn't.
Since you've already stated the intent that Grid< unique_ptr<T> > would indicate unique ownership and a non-copyable T, what about doing something similar with copy on write?
What about explicitly stating when you want to use copy on write with:
Grid< cow_ptr<T> >
A cow_ptr<T> would offer reference counting copies, but on a "non-const dereference" would do a copy of T if the refcount is not 1. So Grid need not worry about memory management to such an extent. It would need only to handle its data buffer, and perhaps move or copy its members around in Grid's copy and/or move members.
A cow_ptr<T> is fairly easily cobbled together by wrapping std::shared_ptr<T>. Here is a partial implementation I put together about a month ago when dealing with a similar issue:
template <class T>
class cow_ptr
{
std::shared_ptr<T> ptr_;
public:
template <class ...Args,
class = typename std::enable_if
<
std::is_constructible<std::shared_ptr<T>, Args...>::value
>::type
>
explicit cow_ptr(Args&& ...args)
: ptr_(std::forward<Args>(args)...)
{}
explicit operator bool() const noexcept {return ptr_ != nullptr;}
T const* read() const noexcept {return ptr_.get();}
T * write()
{
if (ptr_.use_count() > 1)
ptr_.reset(ptr_->clone());
return ptr_.get();
}
T const& operator*() const noexcept {return *read();}
T const* operator->() const noexcept {return read();}
void reset() {ptr_.reset();}
template <class Y>
void
reset(Y* p)
{
ptr_.reset(p);
}
};
I chose to make the "write" syntax very explicit, since COW tends to be more effective when there are very few writes, but many reads/copies. To gain const access, you use it just like any other pointer:
p->inspect(); // compile time error if inspect() isn't const
But to do some modifying operation you have to call it out with the write member function:
p.write()->modify();
shared_ptr has a bunch of really handy constructors and I didn't want to have to replicate all of them in cow_ptr. So the one cow_ptr constructor you see is a poor man's implementation of inheriting constructors that also works for data members.
You may need to fill this out with other smart pointer functionality such as relational operators. You may also want to change how cow_ptr copies a T. I'm currently assuming a virtual clone() function but you could easily substitute into write the use of T's copy constructor instead.
If an explicit Grid< cow_ptr<T> > doesn't really fit your needs, that's all good. I figured I'd share just in case it did.