Какие структуры данных STL с сохраненным неполным типом можно использовать в качестве члена класса?

Насколько мне известно, начиная с C++17, некоторые структуры данных STL могут "существовать" с неполным типом в качестве параметра шаблона, описывающего хранимый тип. Например, я могу использовать std::unique_ptr<Incomplete> (хотя я не уверен, что это структура данных) или std::vector<Incomplete> в качестве членов класса, если все свойства класса (для которых требуется определение Incomplete) реализованы в отдельном файле .cpp:

class Incomplete;
using Complete = int;
class Foo {
private:
  std::unique_ptr<Incomplete> u_p;
  std::vector<Incomplete> v;
  std::deque<Incomplete> d;
  std::list<Incomplete> l;
  std::set<Incomplete> s;
  std::unordered_map<Complete, Complete> u_m_cc;
  std::unordered_map<Complete, Incomplete> u_m_ci;
  std::unordered_map<Incomplete, Complete> u_m_ic;
  std::unordered_map<Incomplete, Incomplete> u_m_ii;
public:
  // implemented in a separate .cpp which has Incomplete defined:
  Foo();
  Foo(Foo&&);
  Foo& operator=(Foo&&);
  Foo(Foo const&);
  Foo& operator=(Foo const&);
  ~Foo();
};

Итак, какие из элементов данных, перечисленных выше, допустимы для такого использования? Как насчет других структур данных, интеллектуальных указателей и т. д.?


person passing_through    schedule 01.02.2020    source источник
comment
Он компилируется или нет? Если нет, какие элементы приводят к диагностическому сообщению?   -  person eerorika    schedule 01.02.2020
comment
@eerorika std::vector и std::unique_ptr в порядке, std::unordered_map отказался работать на моем MSVC: std::pair использует неполную структуру. Даже если он что-то компилирует, не факт, что он будет в стандарте, вот в чем проблема, которую я вижу в таком тестировании.   -  person passing_through    schedule 01.02.2020
comment
Контейнеры STL, размещаемые в куче, будут работать с неполными типами. Те, которые размещены в стеке, не будут и потребуют включения соответствующих заголовков. std::pair — это POD, поэтому любые контейнеры с std::pair в качестве члена не будут работать с неполными типами. Одним интересным исключением из этого является union, где выделение находится в стеке, но только первый член списка объединения должен быть полным типом. Остальные могут быть неполными. Насколько я помню, std::optional использует это свойство.   -  person cplusplusrat    schedule 01.02.2020
comment
@cplusplusrat указано ли это в стандарте C++ 17 (или прежнем)? Означает ли это, что я не могу использовать std::unordered_map с сохраненными неполными типами? Если да, то есть ли альтернатива?   -  person passing_through    schedule 01.02.2020
comment
@walnut: Как тривиальный тип может (автоматически) владеть ресурсами кучи?   -  person Davis Herring    schedule 02.02.2020
comment
@DavisHerring Я имел в виду сам std::pair, а не его членов. Как и любой другой тип, сам std::pair может находиться в стеке или в куче. Я упомянул об этом, потому что первые несколько предложений в комментарии, на который я ответил, звучали для меня так, как будто std::pair тривиальность каким-то образом мешала std::unordered_map принимать неполные типы, что, насколько я могу судить, не так.   -  person walnut    schedule 02.02.2020
comment
Если кому-то интересно: чтобы продолжить просто декларацию вперед, используйте контейнеры boost с выделением кучи (и, очевидно, соответствующие распределители; по умолчанию все в порядке): это разрешено явно.   -  person passing_through    schedule 03.02.2020


Ответы (2)


Предполагая, что ни один из членов классов не используется явно или неявно, пока тип не будет завершен:

Аргумент шаблона всегда может быть неполным для std::unique_ptr и std::shared_ptr, начиная с C++11, см. [unique.ptr]/5 и [util.smartptr.shared]/2 соответственно.

В N4510 добавлена ​​поддержка неполных типов в контейнерах. до C++17, но только для

std::vector
std::list
std::forward_list

и только в том случае, если используемый распределитель удовлетворяет требованиям к полноте распределителя, а именно, что, даже если сам тип значения не является полным, тип распределителя X сам по себе является полным типом, как и все члены std::allocator_traits<X>, кроме ::value_type. Распределитель по умолчанию std::allocator соответствует этим требованиям.

Ни один из других контейнеров нельзя использовать с неполными типами. Согласно предложению, указанному выше, объем был ограничен этими тремя контейнерами «в качестве первого шага», поскольку основные реализации уже поддерживали его.

person walnut    schedule 01.02.2020

начиная с C++17 некоторые структуры данных STL могут «существовать» с неполным типом в качестве параметра шаблона, который описывает сохраненный тип.

Это неправильно.

Начиная с C++17, некоторые типы STL могут объявляться с неполным типом в качестве параметра шаблона.

К моменту создания экземпляров типы должны быть завершены.

Например: (непроверенный код)

struct T; // incomplete
using TV = std::vector<T>; // declared a type using incomplete type T; fine.

TV tv0; // attempt to declare a variable of type TV; fails to compile.

struct T { int v; }; // T is now complete
TV tv1; // compiles
person Marshall Clow    schedule 01.02.2020
comment
Спасибо за исправление. Я до сих пор не понимаю, какое из описанных мной применений допустимо, если таковое имеется. Можешь помочь с этой частью? - person passing_through; 01.02.2020
comment
В примере OP ни один из членов контейнера не создается до тех пор, пока тип не будет завершен. Создание самого std::vector (но не его членов) с неполным типом явно разрешено в C++17. Я не понимаю, как ваше объяснение отвечает на вопрос. - person walnut; 01.02.2020
comment
Типы list, forward_list и vector явно вызываются в C++17. - person Marshall Clow; 01.02.2020