Currently, basic_string does not support custom allocators which use fancy pointers that are not trivially constructible and destructible as the pointer type. This is due to requirements of the internal string representation, __rep. In order to use a pointer type which is not trivially constructible, __rep should:
- be a literal type (required by the implementation). This requires that __rep has at least one constexpr constructor and that its destructor is trivial.
- provide an explicit constructor, copy constructor and copy assignment operator (default special member functions are deleted since __long, a variant member of __rep, is not trivially constructible as it contains a fancy pointer as a member variable).
This change allows the use of fancy pointers which are not trivially constructible by adding an explicit and constexpr constructor, copy constructor and copy assignment operator. It also adds a static assert which will fail if the fancy pointer is not trivially destructible.
Addresses bug report: https://bugs.llvm.org/show_bug.cgi?id=20508
Constant evaluation: From above, we have the requirement that __rep has at least one constexpr constructor. This means that we cannot use non-const functions such as memcpy or memset in the copy constructor / copy assigment operators. Additionally, the active element of a union is not allowed to be changed inside a constexpr constructor. Since during constant evaluation, short string optimization is disabled for basic_string, the active element of the __rep union can be __l and we can manually copy values from other.__l to this->__l.
Non-constant evaluation: During non-constant evaluation, the active member of __rep can be __rep.__s or __rep.__l i.e. a short or long string. In this case, we rely on type punning in the copy constructor and copy assignment operators to read the value of other.__l, even if it isn't the active member. This appears to be legal and also follows the same rationale used when reading / writing memory in __rep.__r.
C++03 / C++11 and on support:
The C++11 standard says "If any non-static data member of a union has a non-trivial default constructor (12.1), copy constructor (12.8), move constructor (12.8), copy assignment operator (12.8), move assignment operator (12.8), or destructor (12.4), the corresponding member function of the union must be user-provided or it will be implicitly deleted (8.4.3) for the union." (https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf, 9.5.2).
However, the C++03 standard says "An object of a class with a non-trivial default constructor (12.1), a non-trivial copy constructor (12.8), a non-trivial destructor (12.4), or a non-trivial copy assignment operator (13.5.3, 12.8) cannot be a member of a union". Since the __long contains a fancy pointer which is not trivially constructible, __long itself is not trivially constructible and therefore __rep cannot include it as a member in C++03. Therefore, fancy pointers can only be supported for C++11 and later.