Еще одно несоответствие clang/gcc относительно использования ODR?


Почему этот код компилируется с GCC (4.9 и 5+), а не с clang (3.5-3.9)?

void test(const int&) { }
int main() {
  const int x = 42;
  auto f = []{ test(x); };
}
У меня есть некоторое смутное представление о том, что расхождение связано с использованием ODR (правила одного определения), но я не понимаю этого достаточно хорошо, чтобы понять, что здесь происходит.
2 13

2 ответа:

x используется odr, поскольку он привязан к ссылке (параметруtest). Поэтому он должен быть захвачен ( [expr.подтянутый.лямбда]/13):

Если лямбда-выражение или экземпляр вызова функции шаблон оператора универсального лямбда Усо-использует ([основная.защита.odr]) this или переменная с автоматической длительностью хранения из области ее достижения, эта сущность должна быть захвачена лямбда-выражением .

Нарушения этого правила, как и все другие правила в стандарте, которые не говорят "не требуется диагностика" или "неопределенное поведение", требуют диагностики.

GCC, к сожалению, выполняет постоянное сворачивание слишком рано, прежде чем он сможет сказать, является ли это использование odr или нет. Это может привести к проблемам , таким как [&]()->const int & { return x; } возврат висящей ссылки.

У T. C. правильный диагноз, вот более четкий фрагмент юридического кодекса, где clang делает правильные вещи, а gcc-нет:

#include <iostream>

void test(const int&a) { std::cout << "in test() -- " << &a << "\n"; }
int main() {
  const int x = 42;
  std::cout << "in main() -- " << &x << "\n";
  auto f = [&]{ test(x); };
  f();
}

Gcc печатает разные адреса для переменной capture-by-reference, чем оригинал!