Почему конструкторы копирования иногда объявляются явно не встроенными?

У меня возникли проблемы с пониманием предложения в отношении встроенной и двоичной совместимости клиентов. Кто-нибудь может объяснить?

C++ FAQ Cline, Lomow:

когда компилятор синтезирует конструктор копирования, он делает их встроенными. Если ваши классы доступны вашим клиентам (например, если ваши клиенты #включают ваши файлы заголовков, а не просто используют исполняемый файл, построенный из ваших классов ), ваш встроенный код копируется в ваш клиенты программы. Если ваши клиенты хотят поддерживать двоичную совместимость между выпусками ваших заголовочных файлов, вы не должны изменять встроенные функции, которые видны клиентам. Из-за этого вам понадобится явная, не встроенная версия конструктора копирования, которая будет использоваться непосредственно клиентом.

5 ответов


бинарная совместимость для динамических библиотек (.dll, .so) часто является важной вещью.

например, вы не хотите перекомпилировать половину программного обеспечения в ОС, потому что вы обновили некоторую библиотеку низкого уровня, которую все использует несовместимым образом (и подумайте, насколько частыми могут быть обновления безопасности). Часто у вас может даже не быть всего исходного кода, необходимого для этого, даже если вы хотите.

для обновлений вашей динамической библиотеки, чтобы быть совместимыми и фактически иметь в результате вы по существу не можете ничего изменить в общедоступном файле заголовка, потому что все, что было скомпилировано в эти другие двоичные файлы напрямую (даже в коде C это часто может включать размеры структуры и макеты членов, и, очевидно, вы не можете удалить или изменить какие-либо объявления функций).

в дополнение к проблемам C, C++ вводит еще много (порядок виртуальных функций,как работает наследование и т. д.) таким образом, можно предположить, что вы можете сделать что-то, что меняет авто сгенерированный c++ конструктор копирования, деструктор и т. д. в противном случае сохраняя совместимость. Если они определены "inline" вместе с классом/структурой, а не явно в вашем источнике, то они будут включены непосредственно другими приложениями/библиотеками, которые связали вашу динамическую библиотеку и использовали эти автоматически сгенерированные функции, и они не получат вашу измененную версию (которая, возможно, даже не поняла, что изменилась!).


ответ говорит, что если сигнатуры задействованных функций не изменились, то "перестройка" программы означает, что объектные файлы должны быть связан снова. Вам не нужно compile их снова.

этой статье описывает понятие бинарная совместимость общих библиотек, реализованных на C++ в системах GNU/Linux. этой ссылке может также помочь вам поймите плюсы и минусы при стремлении к бинарной совместимости при написании библиотеки.

концепция, почему у нас нет виртуальные конструкторы также некоторые связаны с этим.

вас может заинтересовать инструмент, который проверяет совместимость двух заданных версий:abi-compliance-checker для Linux.


это относится к проблемам, которые могут возникнуть между двоичными выпусками библиотеки и изменениями заголовка в этой библиотеке. Есть определенные изменения, которые являются бинарно совместимыми и некоторые изменения, которые не являются. Изменения встроенных функций, таких как встроенный конструктор копирования, не совместимы с двоичными файлами и требуют перекомпиляции кода потребителя.

вы видите это в рамках одного проекта все время. Если вы измените a.cpp тогда вам не нужно перекомпилировать все файлы, которые включают a.hpp. Но если вы измените интерфейс в заголовке, то любой потребитель этого заголовка обычно должен быть перекомпилирован. Это похоже на случай использования общих библиотек.

поддержание двоичной совместимости полезно, когда нужно изменить реализацию двоичной библиотеки без изменения ее интерфейса. Это полезно для таких вещей, как исправления ошибок.

например, скажем, программа использует liba как общая библиотека. Если liba содержит ошибку в методе для класса, который он предоставляет, затем он может изменить внутреннюю реализацию и перекомпилировать общую библиотеку, и программа может использовать новый двоичный выпуск этого liba без перекомпиляции. Если, однако, liba изменяет публичный контракт, такой как реализация встроенного метода или перемещение встроенного метода во внешнее объявление, затем он нарушает двоичный интерфейс приложения (ABI) , и программа consume должна быть перекомпилирована для использования нового двоичная версия liba.


рассмотрим следующий код, скомпилированный в статическую библиотеку:

// lib.hpp
class
t_Something
{
     private: ::std::string foo;

     public: void
     Do_SomethingUseful(void);
};

// lib.cpp
void t_Something::
Do_SomethingUseful(void)
{
    ....
}

// user_project.cpp

int
main()
{
   t_Something something;
   something.Do_SomethingUseful();
   t_Something something_else = something;
}

теперь, когда t_Something поля класса как-то меняются, например, добавляется новый, мы попадаем в ситуацию, когда весь пользовательский код нужно перекомпилировать. В основном конструкторы, неявно сгенерированные компилятором, "просочились" из нашей статической библиотеки в пользовательский код.


думаю, я понимаю, что означает этот отрывок. Но я ни в коем случае не поддерживаю это.

Я считаю, что они описывают сценарий, когда вы разрабатываете библиотеку и предоставляете ее своим клиентам в виде заголовочных файлов и предварительно скомпилированной части двоичной библиотеки. После того, как клиент сделал первоначальную сборку, они, как ожидается, смогут заменить двоичную часть более новой без перекомпиляции их приложения - потребуется только relink. Единственный способ добиться этого было бы гарантировать, что заголовочные файлы неизменяемы, т. е. не изменяются между версиями.

Я думаю, понятие этого будет исходить из того, что в 98 системах сборки не были достаточно умными и не смогли бы обнаружить изменения в файле заголовка и вызвать перекомпиляцию затронутого исходного файла.

все это полностью спорно в настоящее время, и на самом деле, снова идет зерно - поскольку значительное количество библиотек на самом деле стараются быть библиотеками только для заголовков, по нескольким причинам.