Читая документацию по интерфейсу дескриптора узла и изучая некоторые реализации стандартной библиотеки, я заметил, что многие функции типа дескриптора узла можно просто эмулировать с помощью специализации умного указателя std::unique_ptr. На самом деле функциональность типа дескриптора узла очень похожа на функциональность умного указателя std::unique_ptr. Он имеет только интерфейс, более совместимый с функциями ассоциативного контейнера, такими как псевдонимы key_type и mapped_type и функции для получения ссылки на базовое значение или значение ключа/сопоставления.
Итак, есть ли какие-либо другие преимущества, поэтому стандарт ввел тип дескриптора узла по сравнению с простой специализацией std::unique_ptr как node_type?
@PeterHull Я бы предположил, что стандартный дескриптор узла ...
«простая специализация std::unique_ptr» не будет соответствовать спецификации. Это бы испортило трейты и разрешение перегрузок.





С чисто философской точки зрения тот факт, что тип имеет аналогичный интерфейс с другим типом, не означает, что этот тип является избыточным и его следует заменить более общим.
В данном конкретном случае типы просто не эквивалентны. Одна из основных целей node_handle заключается в том, что вы не узнаете фактический тип узла, используемого контейнером. Даже в качестве псевдонима непрозрачного типа; node_type — это специализация node_handle, а не внутреннее имя узла.
Кроме того, учтите, что unique_ptr по своей природе является оберткой для указателя на что-то. Есть указатель, который в какой-то момент не принадлежал, тогда он принадлежит unique_ptr. Следовательно, release — разумная функция: она отказывается от указателя, не уничтожая его.
Это не разумная функция для node_handle. Вы не создавали этот узел, и поэтому вы не способны его уничтожить. Этот узел принадлежит системе, которая намеренно непрозрачна для пользователя. Любой указатель, хранящийся в нем, указывает на структуры данных, определенные реализацией, и их может быть более одной.
Но это игнорирует ключевую особенность node_handle, причину, по которой он вообще существует. Смысл node_handle в том, что вы можете извлечь элемент из такого контейнера, изменить его ключ (и, таким образом, повлиять на то, куда он будет помещен в контейнере), а затем повторно вставить его без выделения памяти. Чтобы сделать это с помощью unique_ptr<T>, потребуется, чтобы T был каким-то типом, который пользователь понимает и с которым может общаться (и, следовательно, используется для изменения ключа). Итак, что такое T?
Это не может быть value_type для контейнера, так как это делает тип ключа const и, следовательно, не поддающимся изменению. Так что это должен быть какой-то новый тип, который дает вам интерфейс для изменения значения ключа. Так как в любом случае это должен быть какой-то новый тип ... вы можете просто сделать node_handle этот тип и избавить себя от кучи боли.
Кроме того, необходимо учитывать поведение распределителя. Во-первых, node_handle также хранит копию распределителя контейнера. Чего unique_ptr не может сделать.
Во-вторых, аллокаторы с отслеживанием состояния могут иметь особое поведение при перемещении-назначении из одного контейнера в другой. В частности, вам может понадобиться переместить распределитель, когда вы это сделаете. Но для многих распределителей вы не можете. Следовательно, node_handle необходимо воспроизвести это поведение. unique_ptr тоже нельзя.
Теперь вы можете достичь всего этого, создав специализацию unique_ptr с другим интерфейсом, соответствующим вышеизложенному, за счет использования скрытых (контейнерных) имен типов в качестве параметров шаблона. Но... зачем заморачиваться? Это другой интерфейс с другим, специализированным назначением.
Так что дайте ему уже собственное имя типа. С++ не исчерпывает имена.
Хороший ответ! Однако абзац «это не может быть value_type для контейнера, так как это делает тип ключа константным и, следовательно, немодифицируемым» неверен, потому что ни std::set, ни std::map не определяют value_type как константу.
@LoS: я не говорил, что они определяют value_type как const. Я сказал "тип ключа". В случае mapvalue_type является pair<const key_type, mapped_type>. А итераторы set не предлагают механизма для изменения его value_type.
Можете ли вы предоставить ссылку на «документацию дескриптора узла»?