почему пользовательский распределитель для std :: string не работает - PullRequest
0 голосов
/ 07 мая 2020

Я определяю новый распределитель следующим образом:

template <class T>
class CodecAlloc: public std::allocator<T> {
public:
  typedef size_t    size_type;
  typedef ptrdiff_t difference_type;
  typedef T*        pointer;
  typedef const T*  const_pointer;
  typedef T&        reference;
  typedef const T&  const_reference;
  typedef T         value_type;

  CodecAlloc() {}
  CodecAlloc(const CodecAlloc&) {}

  pointer allocate(size_type n, const void * = 0) {
              T* t = (T*) malloc(n * sizeof(T));
              std::cout
              << "  used CodecAlloc to allocate   at address "
              << t << " (+)" << std::endl;
              return t;
            }

  void deallocate(void* p, size_type) {
              if (p) {
                free(p);
                std::cout
                << "  used CodecAlloc to deallocate at address "
                << p << " (-)" << 
                std::endl;
              } 
            }

  template <class U>
  struct rebind { typedef CodecAlloc<U> other; };
};

если я typedef новый тип:

typedef std::basic_string<char, std::char_traits<char>, CodecAlloc<char>> String;

он работает хорошо , CodecAllo c :: allocate был вызван . но теперь есть такой интерфейс:

void SetBinary(const std::string&)

его параметр - std :: string, тип String не соответствует. Поэтому я использую этот способ для создания std::string

std::string str = std::string("Hello !!", CodecAlloc<char>);
SetBinary(str)

, но распределитель CodecAllo c не был вызван !! Зачем? и как мне решить эту проблему. интерфейс SetBinary не может быть изменен. спасибо

Ответы [ 3 ]

2 голосов
/ 07 мая 2020

Итак, строка

std::string str = std::string("Hello !!", CodecAlloc<char>);

неверна и содержит несколько ошибок:

  1. Вы не можете использовать CodecAlloc<char>, так как это тип. Вам необходимо передать экземпляр распределителя, поэтому требуется как минимум CodecAlloc<char>().
  2. Если 1. исправлено, он все равно не будет работать, поскольку переданный распределитель отличается от того, который используется std::string. Вы можете исправить это, чтобы использовать свой тип String: std::string str = String("Hello !!", CodecAlloc<char>);, но это не сработает, поскольку std::string не имеет оператора присваивания из типа с другим распределителем - см. this

Итак, с подробным объяснением выше ответ - нет, вы не можете достичь того, чего хотите, и передать настраиваемый typedef'ed std::basic_string в std::string и сохранить свой настраиваемый распределитель.

1 голос
/ 29 мая 2020
std::string str = std::string("Hello !!", CodecAlloc<char>);

Предполагая, что вы действительно создали временный объект типа CodecAlloc<char>() (потому что в противном случае он не будет компилироваться), происходит следующее: вы вызываете конструктор, принимающий const std::allocator<char>& (потому что это std::string::allocator_type) и ваш объект имеет это как базовый класс. Таким образом, ссылка привязывается к базовому подобъекту вашего временного CodecAlloc<char>, а затем этот базовый подобъект копируется во внутренний объект распределителя строки.

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

0 голосов
/ 30 мая 2020

У меня для вас плохие новости.

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

Чтобы понять, почему, вам нужно знать, что такое std::string. Если вы найдете "строку" в cppreference, вы будете перенаправлены на страницу для std::basic_string. Это сделано намеренно, std::basic_string - это шаблон, который определяет набор строковых классов, а std::string - один из них.

Если вы посмотрите на определение std::string, вы увидите, что это определяется как std::basic_string<char>, что является ярлыком для std::basic_string<char, std::char_traits<char>, std::allocator<char>>. Это означает, что std::string уже встроен со своим собственным распределителем , который не расширяется: функции-члены std::allocator<T>::allocate и std::allocator<T>::deallocate не являются виртуальными. Предполагая, что вы хотите использовать этот конструктор из std::basic_string:

basic_string( const CharT* s, const Allocator& alloc = Allocator() );

... это означает, что даже если конструктор использует ссылку на распределитель, эти функции (allocate и deallocate) не будут переопределены: конструктор даже не знает о вашей их версии. Вместо этого он будет использовать версии из базового класса.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...