Что делать, если переопределить встроенную функцию?

я провел дни в странной проблеме и, наконец, обнаружил, что их было два inline функция той же подписи в проекте, и они вызвали проблему. Для упрощения ситуации приведем пример: два cpp файла:

a.cpp

#include <iostream>

void b();

inline void echo()
{
    std::cout << 0 << std::endl;
}

int main()
{
    echo();
    b();
    return 0;
}

и b.cpp

#include <iostream>

inline void echo()
{
    std::cout << 1 << std::endl;
}

void b()
{
    echo();
}

обратите внимание:inline функции echo имеют одинаковую подпись, но разные инструменты. Скомпилировать и запустить

g++ a.cpp b.cpp -o a.out && ./a.out

или как это

g++ a.cpp -c
g++ b.cpp -c
g++ a.o b.o -o a.out
./a.out

печати 0 0. (Для этого я использовал g++ 4.6.1, и я тестировал с clang++ 2.9, тот же результат)

этого не произойдет, если включить оптимизацию, например

g++ -O3 a.cpp b.cpp -o a.out && ./a.out

это 0 1 в этот раз.

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

EDIT:

взгляните на символы в объектном файле

nm a.o b.o | c++filt

оба файла имеют запись echo(). Поэтому я думаю, что проблема возникает во время ссылки. Можно ли сказать, что компоновщик случайным образом выбирает одну реализацию и отбрасывает все остальные?

3 ответов


компилятор не требуется для диагностики этого нарушения ODR, и это не тривиально. The inline ключевое слово означает, что разные единицы перевода могут иметь один и тот же символ, поэтому он отмечен компилятором слабым. Базовый вариант использования-это функция, определенная в заголовке: все единицы перевода, которые включают заголовок, будут иметь определение, и это прекрасно. Компилятору нужно только отбросить все определения, кроме одного, и использовать это определение повсюду.

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

Что касается вашей конкретной проблемы, я не могу знать обоснование, которое привело к тому, что две функции помечены как встроенные, но общая ошибка использует inline ключевое слово для представления оптимизация, а не не жалуйтесь на повторения во время ссылки. The inline ключевое слово имеет смысл в заголовках, но не так много в файлах cpp. В cpp-файлах, если вы хотите разложить некоторый фрагмент кода на вспомогательную функцию, эта функция должна быть либо отмечена static или быть определен в безымянный пространство имен. Если функция static тогда компилятор знает, что все использования этой функции находятся в пределах вашего блок перевода, и он имеет больше знаний, чтобы решить, хочет ли он встроить или нет вызов функции (обратите внимание, что он может встроить, даже если вы не скажете ему, так же, как он может решить не встроить, даже если вы скажете Это).


в стандарте C++ указано, что все определения встроенной функции должны быть одинаковыми, но диагностика не требуется. То есть ваша программа не является допустимой программой на C++, но реализация имеет право не обнаруживать эту ошибку.

См. П. 3.2.5. Это слишком долго, чтобы разместить здесь.


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