GotW # 101 "решение" действительно что-то решает? - PullRequest
21 голосов
/ 21 декабря 2011

Сначала прочитайте сообщения Херта Саттерса 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 ]

9 голосов
/ 21 декабря 2011

Вы правы; в примере, похоже, отсутствует явная реализация шаблона. Когда я пытаюсь запустить пример с конструктором и деструктором для widget::impl в MSVC 2010 SP1, я получаю ошибку компоновщика для pimpl<widget::impl>::pimpl() и pimpl<widget::impl>::~pimpl(). Когда я добавляю template class pimpl<widget::impl>;, он прекрасно связывается.

Другими словами, GotW # 101 удаляет все шаблоны из GotW # 100, но вам нужно добавить явную реализацию шаблона pimpl<...> с реализацией pimpl impl. По крайней мере с № 101 необходимая плита котла проста.

6 голосов
/ 22 декабря 2011

Я думаю, что путаница заключается в следующем: обертка 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.

Бонус / дополнительные преимущества

Как удачно объясняет сам Херб в своем посте ( Почему это улучшение по сравнению с раскрученной вручную идиомой Пимпл? 1 ), есть еще много преимуществ использования этой оболочки pimpl, которые вытекают из повторного использования внутренностей реализации:

  • использовать шаблон для защиты от ненужного попадания в ловушки
  • Вы получаете вариадические, совершенные конструкторы пересылки с C ++ 0x / C ++ 11, и вам не нужно мечтать о том, что шаблонный конструктор шаблонов с переменным списком аргументов с переопределенным значением пересылает пакет аргументов в конструктор упакованных классов отлично и т. д. и т. д.

Короче: СУХОЙ и удобство.


1 , чтобы процитировать (выделено мое):

  • Во-первых, код проще, потому что он устраняет некоторые части шаблон : в версии, свернутой вручную, вы также должны объявить конструктор и записать его тело в файл реализации и явно выделить объект impl. Вы также должны помнить , чтобы объявить деструктор и записать его тело в файл реализации, для неясных причин языка , объясненных в # 100.
  • Во-вторых, код более надежный : в версии, выпущенной вручную, если вы забудете написать деструктор вне строки, Pimpl'd Класс будет скомпилирован изолированно и, по-видимому, будет в состоянии проверки в состоянии, но не сможет скомпилироваться, когда его использует вызывающая программа, которая пытается уничтожить объект и встречает полезное «не может сгенерировать деструктор, потому что impl это, ну, вы знаете, , неполная »ошибка, из-за которой автор вызывающего кода почесывает голову, когда он идет к вам в офис, чтобы выманить вас за проверкой чего-то сломанного.
...