Можно ли создавать стандартные шаблоны контейнеров с неполными типами?
Иногда бывает полезно создать экземпляр стандартного контейнера с неполным типом для получения рекурсивной структуры:
struct multi_tree_node { // Does work in most implementations std::vector child; }; struct trie_node { // Does not work in most implementations std::map next; };
Это работает, потому что в контейнерах нет элементов типа value_type
или функций-членов, которые передают или возвращают объекты value_type
по значению. Стандарт, похоже, не очень много говорит о неполных аргументах шаблона, но есть один бит в C ++ 11 §17.6.4.8 [lib.res.on.functions], «требования к другим функциям»:
В частности, эффекты не определены в следующих случаях: … если неполный тип (3.9) используется в качестве аргумента шаблона при создании экземпляра компонента шаблона, если это специально не разрешено для этого компонента.
Означает ли это, что приведенные выше конструкции незаконны, даже если экземпляры не находятся в блочной области? Подходит ли это к «операциям над типами, используемыми для создания стандартных компонентов шаблона библиотеки» (также 17.6.4.8)? Или реализация библиотеки запрещена для создания экземпляров шаблонов, которые могут завершиться неудачно для неполных типов, когда все требуемые экземпляры будут успешными?
Изменить: поскольку только функции могут вызывать и создавать экземпляры других функций, ограничение «операций с типами …» с теми, которые находятся в области блока, похоже, будет содержать содержимое функций-членов более строгому требованию, чем содержание подписей и определений classов членов. В конце концов, конечно, не имеет смысла ничего делать с помощью multi_tree_node
пока тип не будет завершен. И это распространяется на std::unique_ptr
который явно поддерживает аргумент неполного типа, даже если он используется в области блока .
Редактировать 2: Служит мне правильно, чтобы не потрудиться протестировать пример trie_node
– и я даже пробовал это раньше. Это то же самое, что и пример разрыва в статье , связанный с @Ise. Тем не менее, хотя статья кажется само собой разумеющейся, что «ничего подобного не может работать», решение кажется мне простым std::map
tree_node
class tree_node
std::map
должен быть tree_node
-членом, а не членом classа без шаблона ,
Во всяком случае, эта статья очень хорошо устанавливает намерение дизайна, поэтому я думаю, что моя проблема в том, что она находится под подзаголовком «требований к функциям», это только так.
- Почему необходим allocator :: rebind, когда у нас есть параметры шаблона шаблона?
- static_assert не удается выполнить компиляцию, хотя функция шаблона называется нигде
- (Частично), специализирующийся на несимметричном шаблоне параметра зависимого типа
- heredoc для Windows?
- Предложения по шаблонам электронной почты Java?
- Как компиляторы обрабатывают массивы переменной длины
- Шаблоны: функция шаблона не очень хорошо работает с функцией члена шаблона classа
- Явный экземпляр - когда он используется?
Лично я считаю, что формулировка, созданная в 17.6.4.8/2, немного неоднозначна, но, согласно этой статье , намерение стандарта, похоже, не допускает рекурсивного типа данных с использованием стандартных контейнеров.
В соответствующей заметке VC2005 выдает ошибку для class C { std::deque< C > x; };
class C { std::deque< C > x; };
, тогда как он компилирует class C { std::vector< C > x; };
class C { std::vector< C > x; };
…
Однако, по моему мнению, это ограничение распространяется только на расширение свободы реализации стандартных контейнеров. Как упоминал Kerrek SB , могут быть контейнеры, которые позволяют рекурсивную структуру данных, и Boost.Container, похоже, предоставляет эту возможность.
Вот моя попытка интерпретации:
В стандарте просто говорится, что вы не должны этого делать, хотя любая конкретная реализация может не иметь проблем с поддержкой такой конструкции. Но представьте, например, если кто-то хотел написать оптимизацию «малого вектора», в которой вектор всегда содержит пространство, например, для пяти элементов. Сразу же у вас будут проблемы, потому что у вас будет самореферентный тип. Это было бы проблемой, даже если бы вектор использовал какое-то статическое ветвление в зависимости от размера типа значения.
Поэтому, чтобы не исключать возможности внедрения таких конструкций, стандарт просто говорит, что вы должны использовать только полные типы. Другими словами, тот факт, что большинство контейнеров содержат только ссылки или указатели на тип значения, является детальностью реализации, а не стандартным требованием.
Просто прояснить это: если вы определяете свой собственный шаблон classа, вполне возможно его спроектировать таким образом, чтобы он явно поддерживал неполные типы. Примером стандартного является std::unique_ptr
, который совершенно доволен неполным параметром типа T[]
(или даже void
).