Issue
I got the empty return of std::to_string when I use std::allocator. The code is:
#include <iostream>
#include <memory>
#include <string>
int main()
{
std::allocator<std::string> allocatorStr;
auto const pStr = allocatorStr.allocate(5);
for (int i = 0; i < 5; ++i) { *(pStr + i) = std::to_string(i); }
for (int i = 0; i < 5; ++i) { std::cout << *(pStr + i) << std::endl; }
return 0;
}
But if I change the line *(pStr + i) = std::to_string(i);
to *(pStr + i) = "string";
I could get non-empty output.
I don’t know where is wrong. Thank you very much for your help.
Solution
To quote from cppreference, (emphasis mine):
[
allocate
] … allocatesn * sizeof(T)
bytes of uninitialized storage
In other words, there are no valid std::string
objects there yet and trying to use them (even as the target of a copy) invokes undefined behaviour (I got a segfault).
To fix this, use placement new to construct the needed std::string
objects immediately after calling allocate
:
auto pStr = allocatorStr.allocate(5);
for (int i = 0; i < 5; ++i) { new (pStr + i) std::string; }
...
Then, it works
Edit: Responding to @user17732522’s comment below (which makes a lot of sense), there are advantages to using std::allocator_traits::construct
instead of placement new
; the comment explains what they are. This does essentially the same job as placement new
, but in a more flexible way (from the allocator’s perspective). And since one is doing that, one might as well also use std::allocator_traits::allocate
, since this has a few advantages too.
The code then becomes:
using MyAllocatorTraits = std::allocator_traits <decltype (allocatorStr)>;
auto pStr = MyAllocatorTraits::allocate (allocatorStr, 5);
for (int i = 0; i < 5; ++i) { MyAllocatorTraits::construct (allocatorStr, pStr + i); }
...
Answered By – Paul Sanders
Answer Checked By – Robin (BugsFixing Admin)