Рекурсивные структуры данных и современный С++
От: cppguard  
Дата: 01.02.22 12:43
Оценка:
Как их правильно готовить? Допустим, хотим реализовать trie в виде

struct trie {
  std::unordered_map<char, unique_ptr<trie>> children;
};


но таким образом удаляется коструктор копирования по-умолчанию (из-за присутствия unique_ptr). Можно теперь заменить контейнер указателя на shared_ptr, но тогда все добавляется подсчёт ссылок, время жизни объекта становится непонятным, ещё и барьеры синхронизации (а ведь нам говорили "you don't pay for what you don't use"). Как быть? Пример искусственный, можете представить любую рекурсивную структуру данных.

Если бы, ну чисто теоретически, конструктор копирования по-умолчанию присутствовал, до код построения дерева мог бы быть таким:

struct trie {
  std::unordered_map<char, unique_ptr<trie>> children;
  trie& insert(char c) noexcept {
    auto [i, ignored] = children.try_insert(c, std::make_unique<trie>());
    return *i->second;
  }

  void make_trie(const std::string& s) {
    trie root;
    for (auto c : s) {
      root = root.insert(c);  // compilation error: deleted copy ctor
    }
};


Конечно, я могу слегка модифицировать код, возвращать, например trie* из insert(), а корень объявить тоже как указатель. Типа так
struct trie {
    unordered_map<char, unique_ptr<trie>> children;
    trie* insert(char c) noexcept {
        auto [i, ignored] = children.try_emplace(c, std::make_unique<trie>());
        return i->second.get();
    }
};

int main() {
    string s = "test me";
    trie t;
    trie* tp = &t;
    for (auto c : s) {
         tp = tp->insert(c);
    }
}


Но... это же уродство какое-то А как можно сделать красиво, безопасно и одиоматически?
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.