GotW #101 "решение" действительно что-то решает?
Сначала прочитайте сообщения Херта Саттерса GotW, касающиеся pimpl в C++11:
У меня возникли проблемы с пониманием решения, предложенного в GotW #101. Насколько я понимаю, все проблемы, кропотливо решенные в GotW #100, возвращаются с удвоенной силой:
pimpl
члены являются шаблонами вне линии, и определения не видны в момент использования (вclass widget
определение класса и неявно генерируемые специальные функции-членыwidget
). Там нет никаких явных экземпляров либо. Это приведет к неразрешенным внешним ошибкам при связывании.widget::impl
все еще не завершен в точке, гдеpimpl<widget::impl>::~pimpl()
определен какэкземпляр(я не думаю, что он вообще создан, просто ссылается). Такstd::unique_ptr<widget::impl>::~unique_ptr()
звонкиdelete
на указатель на неполный тип, который производит неопределенное поведение, еслиwidget::impl
имеет нетривиальный деструктор.
Пожалуйста, объясните, что заставляет компилятор генерировать специальные члены в контексте, где widget::impl
завершено. Потому что я не вижу, как это работает.
Если GotW # 101 все еще требует явного определения widget::~widget()
в файле реализации, где widget::impl
завершите, тогда, пожалуйста, объясните комментарий "Более надежный" (который @sehe процитировал в своем ответе).
Я смотрю на основную претензию GotW # 101 о том, что обертка "удаляет некоторые кусочки шаблона", что, как мне кажется (на основе оставшейся части абзаца) означает widget::~widget()
декларация и определение. Поэтому, пожалуйста, не полагайтесь на это в своем ответе, в GotW #101, это прошло!
Трава, если ты зайдешь, пожалуйста, дай мне знать, будет ли нормально вырезать + вставить код решения сюда для справки.
2 ответа
Ты прав; кажется, что в примере отсутствует явная реализация шаблона. Когда я пытаюсь запустить пример с конструктором и деструктором для widget::impl
на MSVC 2010 SP1 я получаю ошибку компоновщика для pimpl<widget::impl>::pimpl()
а также pimpl<widget::impl>::~pimpl()
, Когда я добавлю template class pimpl<widget::impl>;
, это хорошо ссылки.
Другими словами, GotW #101 удаляет все шаблоны из GotW #100, но вам нужно добавить явное создание экземпляра pimpl<...>
шаблон с реализацией pimpl
осущ. По крайней мере, с № 101 плита, которая вам нужна, проста.
Я думаю, что путаница заключается в следующем: оболочка pimpl может быть шаблоном, а класс виджета - нет:
demo.h
#include "pimpl_h.h"
// in header file
class widget {
public:
widget();
~widget();
private:
class impl;
pimpl<impl> pimpl_;
};
demo.cpp
#include "demo.h"
#include "pimpl_impl.h"
// in implementation file
class widget::impl {
// :::
};
widget::widget() : pimpl_() { }
widget::~widget() { } // or =default
Как видите, никто никогда не увидит "шаблонный" конструктор для класса виджета. Будет только одно его определение, и нет необходимости в "явной реализации".
И наоборот, ~pimpl<>
деструктор только когда-либо создается из единственной точки определения ~widget()
деструктор. На этом этапе impl
класс завершен по определению.
Нет ошибок связи и нарушений ODR/UB.
Бонус / дополнительные преимущества
Как удачно объясняет сам Херб в своем посте ( Почему это улучшение по сравнению с раскрученным вручную языком Pimpl?), Есть много других преимуществ использования этой оболочки pimpl, которые вытекают из повторного использования внутренностей реализации:
- используйте шаблон, чтобы избежать ненужных ошибок
- Вы получаете variadic, совершенные конструкторы пересылки с C++0x/C++11, вам не нужно мечтать о том, что шаблонный конструктор шаблонов с переменным списком аргументов с переменной rvalue перенаправляет пакет аргументов в конструктор упакованных классов и т. д. и т. д. и т. д.
Короче: СУХОЙ и удобство.
1 процитировать (выделение мое):
- Во-первых, код проще, потому что он устраняет некоторые фрагменты: в ручной версии вы также должны объявить конструктор и записать его тело в файл реализации и явно выделить объект impl. Вы также должны помнить, чтобы объявить деструктор и записать его тело в файле реализации, по непонятным языковым причинам, объясненным в #100.
- Во-вторых, код более устойчив: в ручной версии, если вы забудете написать деструктор вне строки, класс Pimpl'd будет скомпилирован изолированно и, по-видимому, находится в состоянии для регистрации. но не скомпилируется, когда используется вызывающей программой, которая пытается уничтожить объект и сталкивается с полезной ошибкой "не может сгенерировать деструктор, потому что impl является, ну, вы знаете, неполной" ошибкой, из-за которой автор вызывающего кода почесывает голову, пока он идет в ваш офис, чтобы вытащить вас за тем, чтобы проверить, что сломано.