Подтвердить что ты не робот

Копирование объектов с использованием разных распределителей в С++

Итак, у меня есть хороший постоянный класс распределителя persistent_alloc<T>, который позволяет мне выделять объекты и строки контейнера С++ в постоянную память, которая поддерживается файлом mmaped, который может сохраняться от одного запуска моей программы до следующего.

Моя проблема возникает, когда я хочу сделать что-нибудь, что смешивает постоянные и не стойкие объекты. Например, у меня есть

typedef std::basic_string<char, std::char_traits<char>, persistent_alloc<char>> pstring;

pstring a, b, c;
std::string x, y, z;

Я хочу иметь возможность делать такие вещи, как:

if (a == x)
    a = y;
c = z + b;

и т.д., но по умолчанию он не работает, поскольку pstring и std::string являются несвязанными типами. Теперь, что касается сравнения, я могу определить:

template<typename Alloc1, typename Alloc2> inline bool
operator==(const std::basic_string<char, std::char_traits<char>, Alloc1> &a,
           const std::basic_string<char, std::char_traits<char>, Alloc2> &b)
{
    return strcmp(a.c_str(), b.c_str()) == 0;
}

... и теперь я могу сравнить строки для равенства. Но добавление их для каждой операции кажется болью - кажется, что они ДОЛЖНЫ быть предоставлены стандартной библиотекой. Хуже того, операторы присваивания и конструкторы копирования должны быть членами и не могут быть определены как глобальные встроенные функции, подобные этому.

Есть ли разумный способ сделать это? Или мне нужно эффективно переписать всю стандартную библиотеку для удобства использования распределителей?

4b9b3361

Ответ 1

Есть способ справиться с этим, но вам нужно немного подумать о коробке. Вам нужен промежуточный тип, который неявно можно построить как из std::string, так и из вашей строки распределителя.

В настоящее время перед комитетом С++ есть предложение для такого вещания. Он основан на уже существующей версии Apache с лицензией Google, которая уже существует. Он назывался basic_string_ref; это класс шаблона, который в основном является указателем на первый символ в строке и размером, представляющим длину строки. Это не настоящий контейнер в том смысле, что он не управляет памятью.

Это именно то, что вам нужно.

basic_string_ref для определенного типа символа и типа признаков неявно можно построить из std::basic_string независимо от распределителя.

Все операторы сравнения могут быть определены в терминах basic_string_ref. Так как он неявно конструктивен из std::basic_string (и фактически свободен для построения), он будет работать прозрачно для сравнения между выделенными друг другу строками.

Выполнение задания довольно сложнее, но выполнимо. Для этого требуется серия преобразований:

a = pstring{basic_string_ref{y}};

Не самый красивый код, конечно. Мы бы предпочли просто изменить конструктор копирования и оператор присваивания std::basic_string как агностик-распределитель. Но так как это не выполнимо, это действительно самое лучшее. Вы можете даже обернуть его в функцию шаблона:

template<typename DestAllocator, typename SourceAllocator, typename charT, typename traits>
std::basic_string<charT, traits, DestAllocator> conv_str(const std::basic_string<charT, traits, SourceAllocator> &input)
{
  return std::basic_string<charT, traits, DestAllocator>{basic_string_ref<charT, traits>{y}};
}

Конечно, если вы можете это сделать, вы можете просто сделать это:

template<typename DestAllocator, typename SourceAllocator, typename charT, typename traits>
std::basic_string<charT, traits, DestAllocator> conv_str(const std::basic_string<charT, traits, SourceAllocator> &input)
{
  return std::basic_string<charT, traits, DestAllocator>{y.begin(), y.end()};
}

Было бы здорово, если бы это было всего лишь частью std::basic_string, так что вам не нужны обходные пути. Но это не так.