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 является, ну, вы знаете, неполной" ошибкой, из-за которой автор вызывающего кода почесывает голову, пока он идет в ваш офис, чтобы вытащить вас за тем, чтобы проверить, что сломано.
Другие вопросы по тегам