Предположим, у меня есть три скомпилированных объекта, созданных одним и тем же компилятором / версией :
- A был скомпилирован со стандартом C ++ 11
- B был скомпилирован по стандарту C ++ 14
- C был скомпилирован со стандартом C ++ 17
Для простоты предположим, что все заголовки были написаны на C ++ 11 с использованием только конструкций, семантика которых не изменилась между всеми тремя стандартными версиями , и поэтому любые взаимозависимости были правильно выражены с включением заголовка, и компилятор не возражал.
Какие это комбинации этих объектов и небезопасно ли объединить их в один двоичный файл? Зачем?
РЕДАКТИРОВАТЬ: приветствуются ответы, касающиеся основных компиляторов (например, gcc, clang, vs ++)
std::string
реализация в libstdc ++ не зависит от используемого-std
режима . Это важное свойство именно для поддержки таких ситуаций, как ОП. Вы можете использовать новыйstd::string
код в C ++ 03 и старыйstd::string
код в C ++ 11 (см. Ссылку в последующем комментарии Маттео).Ответы:
Для GCC безопасно связывать вместе любую комбинацию объектов A, B и C. Если все они построены с одной и той же версией, то они совместимы с ABI, стандартная версия (т.е.
-std
опция) не имеет никакого значения.Зачем? Потому что это важное свойство нашей реализации, над которым мы много работаем.
Проблемы возникают в том случае, если вы связываете вместе объекты, скомпилированные с разными версиями GCC, и использовали нестабильные функции из нового стандарта C ++ до того, как GCC поддерживает этот стандарт. Например, если вы скомпилируете объект с использованием GCC 4.9
-std=c++11
и другого объекта с GCC 5, у-std=c++11
вас возникнут проблемы. Поддержка C ++ 11 была экспериментальной в GCC 4.x, поэтому между GCC 4.9 и 5 версиями функций C ++ 11 произошли несовместимые изменения. Точно так же, если вы скомпилируете один объект с GCC 7, а-std=c++17
другой объект с GCC 8, у-std=c++17
вас возникнут проблемы, потому что поддержка C ++ 17 в GCC 7 и 8 все еще экспериментальная и развивается.С другой стороны, любая комбинация следующих объектов будет работать (хотя см. Примечание ниже о
libstdc++.so
версии):-std=c++03
-std=c++11
-std=c++17
Это связано с тем, что поддержка C ++ 03 стабильна во всех трех используемых версиях компилятора, и поэтому компоненты C ++ 03 совместимы между всеми объектами. Поддержка C ++ 11 стабильна, начиная с GCC 5, но объект D не использует никаких функций C ++ 11, а объекты E и F используют версии, в которых поддержка C ++ 11 стабильна. Поддержка C ++ 17 нестабильна ни в одной из используемых версий компилятора, но только объект F использует функции C ++ 17, поэтому нет проблем совместимости с двумя другими объектами (единственные функции, которые они разделяют, взяты из C ++ 03 или C ++ 11, и используемые версии делают эти части в порядке). Если позже вы захотите скомпилировать четвертый объект, G, используя GCC 8,
-std=c++17
тогда вам нужно будет перекомпилировать F с той же версией (или без ссылки на F), потому что символы C ++ 17 в F и G несовместимы.Единственное предостережение относительно описанной выше совместимости между D, E и F заключается в том, что ваша программа должна использовать
libstdc++.so
разделяемую библиотеку из GCC 7 (или новее). Поскольку объект F был скомпилирован с помощью GCC 7, вам необходимо использовать общую библиотеку из этого выпуска, потому что компиляция любой части программы с помощью GCC 7 может привести к появлению зависимостей от символов, которых нет вlibstdc++.so
GCC 4.9 или GCC 5. Аналогичным образом, если вы связались с объектом G, созданным с помощью GCC 8, вам нужно будет использоватьlibstdc++.so
GCC 8, чтобы убедиться, что все символы, необходимые для G, найдены. Простое правило - обеспечить, чтобы общая библиотека, которую программа использует во время выполнения, была как минимум такой же новой, как версия, используемая для компиляции любого из объектов.Еще одно предостережение при использовании GCC, уже упоминавшееся в комментариях к вашему вопросу, заключается в том, что, начиная с GCC 5, в libstdc ++ доступны две реализации
std::string
. Две реализации несовместимы по ссылкам (у них разные искаженные имена, поэтому их нельзя связать вместе), но они могут сосуществовать в одном двоичном файле (у них разные искаженные имена, поэтому не конфликтуйте, если один объект используетstd::string
и другое использованиеstd::__cxx11::string
). Если ваши объекты используют,std::string
то обычно все они должны быть скомпилированы с одной и той же строковой реализацией. Скомпилируйте с,-D_GLIBCXX_USE_CXX11_ABI=0
чтобы выбрать исходнуюgcc4-compatible
реализацию или-D_GLIBCXX_USE_CXX11_ABI=1
выбрать новуюcxx11
реализацию (не обманывайте себя именем, его можно использовать и в C ++ 03, он называетсяcxx11
потому что он соответствует требованиям C ++ 11). Какая реализация используется по умолчанию, зависит от того, как был настроен GCC, но значение по умолчанию всегда можно переопределить во время компиляции с помощью макроса.источник
Ответ состоит из двух частей. Совместимость на уровне компилятора и совместимость на уровне компоновщика. Начнем с первого.
Использование одного и того же компилятора означает, что одни и те же заголовок стандартной библиотеки и исходные файлы (те, которые связаны с компилятором) будут использоваться независимо от целевого стандарта C ++. Следовательно, файлы заголовков стандартной библиотеки написаны для совместимости со всеми версиями C ++, поддерживаемыми компилятором.
Тем не менее, если параметры компилятора, используемые для компиляции единицы перевода, указывают конкретный стандарт C ++, то любые функции, доступные только в новых стандартах, не должны быть доступны. Это делается с помощью
__cplusplus
директивы. Смотрите в исходном векторном файле интересный пример того, как он используется. Точно так же компилятор отклонит любые синтаксические функции, предлагаемые более новыми версиями стандарта.Все это означает, что ваше предположение применимо только к написанным вами заголовочным файлам. Эти файлы заголовков могут вызывать несовместимость при включении в разные единицы перевода, ориентированные на разные стандарты C ++. Это обсуждается в Приложении C стандарта C ++. Всего 4 пункта, я остановлюсь только на первом, а вкратце расскажу об остальных.
C.3.1 Раздел 2: лексические соглашения
Одиночные кавычки разделяют символьный литерал в C ++ 11, тогда как в C ++ 14 и C ++ 17 они являются разделителями цифр. Предположим, у вас есть следующее определение макроса в одном из файлов заголовков на чистом C ++ 11:
#define M(x, ...) __VA_ARGS__ // Maybe defined as a field in a template or a type. int x[2] = { M(1'2,3'4) };
Рассмотрим две единицы трансляции, которые включают файл заголовка, но нацелены на C ++ 11 и C ++ 14 соответственно. При ориентации на C ++ 11 запятая в кавычках не считается разделителем параметров; есть только один параметр. Следовательно, код будет эквивалентен:
int x[2] = { 0 }; // C++11
С другой стороны, при ориентации на C ++ 14 одинарные кавычки интерпретируются как разделители цифр. Следовательно, код будет эквивалентен:
int x[2] = { 34, 0 }; // C++14 and C++17
Дело здесь в том, что использование одинарных кавычек в одном из файлов заголовков чистого C ++ 11 может привести к неожиданным ошибкам в единицах перевода, предназначенных для C ++ 14/17. Следовательно, даже если файл заголовка написан на C ++ 11, его нужно писать осторожно, чтобы обеспечить совместимость с более поздними версиями стандарта.
__cplusplus
Директива может быть полезным здесь.Остальные три пункта стандарта включают:
C.3.2 Раздел 3: основные концепции
C.3.3 Раздел 7: декларации
C.3.4 Раздел 27: библиотека ввода / вывода
Возможная несовместимость между C ++ 14 и C ++ 17 обсуждается в C.4. Поскольку все нестандартные файлы заголовков написаны на C ++ 11 (как указано в вопросе), этих проблем не возникнет, поэтому я не буду их здесь упоминать.
Теперь обсудим совместимость на уровне компоновщика. В общем, потенциальные причины несовместимости включают следующее:
main
точка входа.Если формат результирующего объектного файла зависит от целевого стандарта C ++, компоновщик должен иметь возможность связывать различные объектные файлы. К счастью, в GCC, LLVM и VC ++ это не так. То есть формат файлов объектов один и тот же независимо от целевого стандарта, хотя он сильно зависит от самого компилятора. Фактически, ни один из компоновщиков GCC, LLVM и VC ++ не требует знаний о целевом стандарте C ++. Это также означает, что мы можем связывать уже скомпилированные объектные файлы (статически связывая среду выполнения).
Если процедура запуска программы (вызываемая функция
main
) отличается для разных стандартов C ++ и разные процедуры несовместимы друг с другом, то связать объектные файлы будет невозможно. К счастью, в GCC, LLVM и VC ++ это не так. Кроме того, сигнатураmain
функции (и ограничения, которые применяются к ней, см. Раздел 3.6 стандарта) одинакова для всех стандартов C ++, поэтому не имеет значения, в какой единице перевода она существует.Как правило, WPO может плохо работать с объектными файлами, скомпилированными с использованием различных стандартов C ++. Это зависит от того, какие именно этапы компилятора требуют знания целевого стандарта, а какие - нет, а также от того, какое влияние он оказывает на межпроцедурные оптимизации, перекрестные с объектными файлами. К счастью, GCC, LLVM и VC ++ хорошо спроектированы и не имеют этой проблемы (насколько мне известно).
Поэтому GCC, LLVM и VC ++ были разработаны для обеспечения двоичной совместимости между различными версиями стандарта C ++. Однако на самом деле это не является требованием самого стандарта.
Кстати, хотя компилятор VC ++ предлагает переключатель std , который позволяет настроить таргетинг на конкретную версию стандарта C ++, он не поддерживает таргетинг на C ++ 11. Минимальная версия, которую можно указать, - C ++ 14, которая используется по умолчанию, начиная с Visual C ++ 2013 Update 3. Вы можете использовать старую версию VC ++ для таргетинга на C ++ 11, но тогда вам придется использовать другие компиляторы VC ++. для компиляции разных единиц перевода, предназначенных для разных версий стандарта C ++, что, по крайней мере, нарушит WPO.
CAVEAT: Мой ответ может быть не полным или очень точным.
источник
Новые стандарты C ++ состоят из двух частей: языковых функций и стандартных библиотечных компонентов.
Как вы подразумеваете под новым стандартом , изменения в самом языке (например, ранжированные) почти не вызывают проблем (иногда возникают конфликты в заголовках сторонних библиотек с новыми стандартными языковыми функциями).
Но стандартная библиотека ...
Каждая версия компилятора поставляется с реализацией стандартной библиотеки C ++ (libstdc ++ с gcc, libc ++ с clang, стандартная библиотека MS C ++ с VC ++, ...) и только одной реализацией, а не многими реализациями для каждой стандартной версии. Также в некоторых случаях вы можете использовать другую реализацию стандартной библиотеки, отличную от предоставленной компилятором. Вам следует позаботиться о том, чтобы связать более старую реализацию стандартной библиотеки с более новой.
Конфликт, который может возникнуть между сторонними библиотеками и вашим кодом, - это стандартная библиотека (и другие библиотеки), которая связана с этими сторонними библиотеками.
источник