Предположим, что используется следующий исходный файл (единица перевода; TU):
struct X {
int i;
X(const X&);
X(X&&);
};
X::X(const X&) = default;
X::X(X&&) = default
Если я скомпилирую его с помощью Clang, он сгенерирует машинный код для конструкторов копирования и перемещения. Однако GCC генерирует его только для конструктора перемещения, а машинный код конструктора копирования отсутствует в результирующем объектном файле. Сначала я подумал, что это какая-то проблема, связанная с Compiler Explorer, но затем я заметил такое же поведение в своей локальной системе Linux, используя objdump.
Живая демонстрация: https://godbolt.org/z/1re4brr6P
Я этого не понимаю, потому что, раз у меня есть еще одно ТУ с вызовом конструктора копирования, его нужно вызывать из машинного кода: https://godbolt.org/z/3YaMTd5do. Итак, GCC генерирует вызов конструктора копирования во втором TU, но не генерирует свой машинный код в первом TU. Как тогда компоновщик может связать объектные файлы вместе?
@PepijnKramer Как компоновщик может связать вызов функции с ее машинным кодом, если этот машинный код отсутствует? Извините, но я не понимаю, какое отношение ваш комментарий имеет к заданной проблеме. Не могли бы вы уточнить, какая именно часть этого выступления объясняет это?
@DanielLangr Вам также следует опубликовать сборку здесь, в своем посте.
gcc просто объединяет оба конструктора в один фрагмент машинного кода. Там есть символ копирования, Godbolt просто отфильтровывает его. Используйте nm -C myfile.o локально, чтобы увидеть это.
@DanielLangr Извините, кажется, я действительно неправильно понял вопрос.
@n.m.couldbeanAI: вы также можете отключить фильтрацию директив в Godbolt, GCC часто использует .set foo, bar, чтобы определить foo как имеющий тот же адрес, что и bar.





Это просто проблема с тем, как проводник компилятора представляет выходные данные (как и objdump):
GCC определяет символы для обоих конструкторов по одному и тому же адресу, поскольку функции ведут себя одинаково. Таблица символов:
$ nm test.o
0000000000000000 T _ZN1XC1EOS_
0000000000000000 T _ZN1XC1ERKS_
0000000000000000 T _ZN1XC2EOS_
0000000000000000 T _ZN1XC2ERKS_
$ nm -C test.o
0000000000000000 T X::X(X&&)
0000000000000000 T X::X(X const&)
0000000000000000 T X::X(X&&)
0000000000000000 T X::X(X const&)
Обычно компилятор не может оптимизировать две функции, чтобы они имели один и тот же адрес, и вместо этого компилирует одну из идентичных функций в одну инструкцию перехода в другую, но для конструктора (и другого нестатического (неявного объекта) члена функции), это нормально, потому что в C++ нет возможности наблюдать адрес конструктора.
В проводнике компилятора, если вы просматриваете уровень сборки, а не дизассемблированный двоичный файл, вы также можете отключить фильтрацию директив ассемблера с помощью третьего значка слева в окне компилятора. Затем вы увидите директивы, которые заставляют все эти символы определяться в одном и том же месте. К сожалению, директив будет много, поэтому читать будет немного сложно. Это будет невозможно в режимах «Компиляция в двоичный объект»/«Ссылка на двоичный объект».
Вы можете отключить фильтрацию директив в раскрывающемся списке Godbolt, GCC часто использует .set foo, bar, чтобы определить foo как имеющий тот же адрес, что и bar. Фильтр Godbolt по умолчанию удаляет директивы .set.
Это результат различных оптимизаций, используемых компиляторами -O2.
Таким образом, в основном те, которые должны функционировать, были объединены в одно, поскольку полученный машинный код идентичен.
Использование коллекции компиляторов GNU (GCC): параметры оптимизации
-fipa-icfВыполните идентичное свертывание кода для функций и переменных, доступных только для чтения. Оптимизация уменьшает размер кода и может помешать раскручиванию стеков из-за замены функции эквивалентной с другим именем. Оптимизация работает более эффективно, если включена оптимизация времени соединения.
Тем не менее, поведение похоже на оптимизацию Gold Linker ICF, GCC ICF работает на разных уровнях, и поэтому оптимизации не одинаковы - есть эквиваленты, которые находит только GCC, и эквиваленты, найденные только Gold.
Этот флаг включен по умолчанию в -O2 и -Os.
Машинный код генерируется в объектный файл для каждого TU. Включив некоторую информацию об имени функции, компоновщик сможет узнать, какие части объектного файла нужно связать с конечным exe-файлом. Для более подробной информации: Назад к основам: компиляция и связывание – Бен Сакс – CppCon 2021