После разговора о распределителях на CppCon я наткнулся на следующий фрагмент кода :
#include <iostream>
#include <string>
#include <utility>
#include <vector>
namespace {
template <typename T>
class MyAllocator {
public:
using value_type = T;
MyAllocator(std::string iType) : _type(std::move(iType)) {}
T* allocate(const std::size_t iNo) { return new T[iNo]; }
void deallocate(T* iPtr, const std::size_t) { delete[] iPtr; }
constexpr bool operator!=(const MyAllocator& oth) const {
return _type != oth._type;
}
const std::string& getType() const noexcept { return _type; }
private:
std::string _type;
};
using MyString =
std::basic_string<char, std::char_traits<char>, MyAllocator<char>>;
} // anonymous namespace
int main(int, char**) {
::MyString str1(::MyAllocator<char>("ForStr1"));
::MyString str2(::MyAllocator<char>("ForStr2"));
::MyString str3(::MyAllocator<char>("ForStr3"));
std::vector<::MyString> aVector;
aVector.reserve(1024);
aVector.push_back(str1);
aVector.push_back(str2);
std::cout << "[0]: " << aVector[0].get_allocator().getType() << "\n"
<< "[1]: " << aVector[1].get_allocator().getType() << "\n";
aVector.insert(aVector.begin(), str3);
const auto& type0 = aVector[0].get_allocator().getType();
const auto& type1 = aVector[1].get_allocator().getType();
const auto& type2 = aVector[2].get_allocator().getType();
std::cout << "[0]: " << type0 << "\n"
<< "[1]: " << type1 << "\n"
<< "[2]: " << type2 << "\n";
return 0;
}
Я думаю,общая тема здесь о " распределителях во вложенных контейнерах ".Хотя с функциональной точки зрения у меня возникает проблема, я не могу понять, что произошло в коде.
В коде у нас есть пользовательский allocator , который, по сути, ведет себя как распределитель по умолчанию, за исключением еговнутренне хранит вид данных.
Я строю три разных строк с тремя различными экземплярами одного и того же распределителя:
using MyString =
std::basic_string<char, std::char_traits<char>, MyAllocator<char>>;
::MyString str1(::MyAllocator<char>("ForStr1"));
::MyString str2(::MyAllocator<char>("ForStr2"));
::MyString str3(::MyAllocator<char>("ForStr3"));
Теперь яиметь простое std::vector<MyString>
:
std::vector<::MyString> aVector;
aVector.reserve(1024);
Я зарезервировал пространство, чтобы избежать перераспределение .
Теперь я нажимаю первые две строки:
aVector.push_back(str1);
aVector.push_back(str2);
std::cout << "[0]: " << aVector[0].get_allocator().getType() << "\n"
<< "[1]: " << aVector[1].get_allocator().getType() << "\n";
// As expected, it prints:
// [0]: ForStr1
// [1]: ForStr2
Результат печати - то, что я ожидаю.Я предполагаю, что распределитель принадлежит контейнеру std::string
.
Однако , если я принудительно выполню некоторые операции копирования / перемещения (перестановки) с помощью:
aVector.insert(aVector.begin(), str3);
// Now we have vector be like:
// [str3:ForStr3] [str1:ForStr1] [str2:ForStr2]
Затем,распределители, связанные со строками внутри вектора, кажутся поврежденными:
const auto& type0 = aVector[0].get_allocator().getType();
const auto& type1 = aVector[1].get_allocator().getType();
const auto& type2 = aVector[2].get_allocator().getType();
std::cout << "[0]: " << type0 << "\n"
<< "[1]: " << type1 << "\n"
<< "[2]: " << type2 << "\n";
Он печатает:
[0]: ForStr1
[1]: ForStr2
[2]: ForStr2
Я ожидаю:
[0]: ForStr3
[1]: ForStr1
[2]: ForStr2
Почему это поведение?Есть ли UB , которые я пропустил?Распределитель, связанный с std::string
, является частью самого объекта, не так ли?