Почему эта неопределенная внешняя переменная не приводит к ошибке компоновщика в С++ 17?

Я скомпилировал и запустил следующую программу в компиляторе C++17 (Coliru). В программе я объявил переменную extern, но не определил ее. Однако компилятор не выдает ошибку компоновщика.

#include <iostream>

extern int i; // Only declaration

int func() 
{
    if constexpr (true)
        return 0;
    else if (i)
        return i;
    else
        return -1;
}

int main() 
{
    int ret = func();
    std::cout<<"Ret : "<<ret<<std::endl;
}

Почему компилятор не выдает ошибку компоновщика?


person msc    schedule 11.07.2017    source источник


Ответы (4)


Потому что переменная не используется odr. У вас есть constexpr if, который всегда отбрасывает ветку, которая может его использовать.

Одним из пунктов constexpr if является то, что отброшенная ветвь даже не должна компилироваться, а должна быть правильно сформирована. Вот как мы можем размещать вызовы несуществующих функций-членов в отброшенной ветке.

person StoryTeller - Unslander Monica    schedule 11.07.2017
comment
Если я изменю constexpr if на обычный if, то я также не получу ошибку компоновщика. Почему? - person Rakete1111; 11.07.2017
comment
@ Rakete1111 - Думаю, агрессивная и нестандартная оптимизация. Он не связывается, когда у оптимизатора нет другого выбора, кроме как обратиться к переменной ideone.com/NjEqoy. - person StoryTeller - Unslander Monica; 11.07.2017
comment
@ Rakete1111 - На самом деле clang и GCC только что приняли это для меня. Я в тупике. - person StoryTeller - Unslander Monica; 11.07.2017
comment
@ Rakete1111 - у AnT есть цитата в их ответе. Диагностика не требуется. - person StoryTeller - Unslander Monica; 11.07.2017
comment
Я думаю, что GCC и clang рассматривают внешнее объявление как слабое определение для совместимости со старым нестандартным кодом. - person rodrigo; 12.07.2017

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

3.2 Правило с одним определением

4 Каждая программа должна содержать ровно одно определение каждой невстроенной функции или переменной, которая используется в этой программе за пределами отброшенного оператора (6.4.1); диагностика не требуется.

Спецификация языка понимает, что оптимизирующий компилятор может быть достаточно умен, чтобы исключить все виды использования переменной odr. В этом случае было бы чрезмерным и ненужным требовать от реализации обнаружения потенциальных нарушений ODR и сообщения о них.

person AnT    schedule 11.07.2017
comment
Даже если диагностика не требуется, не означает ли это, что программа плохо сформирована и, следовательно, не должна проходить компиляцию и компоновку? - person Hatted Rooster; 11.07.2017
comment
@Rick Astley: Плохо сформированный никогда не означает, что не следует проходить компиляцию и компоновку. Реализациям разрешено успешно компилировать абсолютно все, если они выдают диагностику там, где диагностика требуется спецификацией языка. На самом деле, единственной функцией, которая требуется для фактического прекращения компиляции в C++, является директива #error. - person AnT; 11.07.2017
comment
@AnT: почти правильно, за исключением того, что компилятору даже не нужно останавливать обработку после #error, требуется только выдать диагностику, а программа плохо сформирована (тогда как #warning не отображает программа некорректна). См. §16.5 [N4140, cpp.error]. (Обновление: #warning не является частью стандарта.) - person Arne Vogel; 11.07.2017
comment
Есть еще static_assert, но это неважно. - person Deduplicator; 11.07.2017
comment
@Deduplicator static_assert(!"do not format harddrive"); может создать исполняемый файл, который форматирует жесткий диск. Он также должен генерировать диагностику, но стандарт не устанавливает никаких ограничений на то, что компилятор делает с неправильно сформированной программой. Не то чтобы это имело значение, так как это может сделать только враждебный компилятор. - person Yakk - Adam Nevraumont; 11.07.2017

Поскольку компилятор выдает ошибки компилятора, компоновщик выдает ошибки компоновщика...

Нет, серьезно:

if constexpr (true)

всегда истинно, поэтому компилятор игнорирует остальную часть предложения if, потому что оно никогда не достигается. Так что i на самом деле никогда не используется.

person Rene    schedule 11.07.2017

На этот вопрос уже был дан ответ, но если вам интересно, cppreference.com имеет именно этот пример для constexpr if:

Constexpr Если

Оператор, начинающийся с if constexpr, известен как оператор constexpr if.

В операторе constexpr if значение условия должно быть контекстно преобразованным константное выражение типа bool. Если значение равно true, то statement-false отбрасывается (если присутствует), в противном случае statement-true отбрасывается.
[...]
Отброшенный оператор может odr-использовать переменную, которая не определенный:

extern int x; // no definition of x required
int f() {
if constexpr (true)
    return 0;
else if (x)
    return x;
else
    return -x;
}
person Fabio says Reinstate Monica    schedule 25.08.2017