Что если переопределить встроенную функцию? - PullRequest
8 голосов
/ 27 июля 2011

Я провел дни в странной проблеме и, наконец, обнаружил, что в проекте было две функции 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 ]

13 голосов
/ 27 июля 2011

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

См. Пункт 3.2.5. Здесь слишком долго писать.

6 голосов
/ 27 июля 2011

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

5 голосов
/ 27 июля 2011

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

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

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

...