неопределенная ссылка на статическую структуру constexpr класса, g ++ против clang - PullRequest
0 голосов
/ 21 ноября 2018

Это мой код, a.cpp

struct int2
{
    int x, y;
};
struct Foo{
    static constexpr int bar1 = 1;
    static constexpr int2 bar2 = {1, 2};
};
int foo1(){
    return Foo::bar1; // this is ok for both clang++ and g++
}
int2 foo2(){
    return Foo::bar2; // undefined reference to `Foo::bar2' in clang++
}
int main(){ std::cout << foo2().x << std::endl; return 0; }

использовать clang для компиляции, clang++ -std=c++11 a.cpp

/tmp/a-0dba90.o: In function `foo2()':
a.cpp:(.text+0x18): undefined reference to `Foo::bar2'
clang-7: error: linker command failed with exit code 1 (use -v to see 
invocation)

g++ -std=c++11 a.cpp не выдает ошибок.

У меня вопрос,

  1. , кто прав по приведенному выше коду?clang или g ++?
  2. почему bar2 неверен, а bar1 корректен в clang?

версия компилятора: g ++ 5.4.0 и clang 7.0.0

UPDATE:Вопрос помечается как дубликат , другой вопрос , но это не так.Я знаю, что мог бы явно добавить определение вне класса, чтобы получить его за Clang.Этот вопрос о том, почему вся разница между g ++ и clang.

Ответы [ 2 ]

0 голосов
/ 21 ноября 2018

Похоже, вы предполагаете, что если один компилятор прав, то другой должен ошибаться.Программа либо содержит ошибку (а затем компилятор, который принимает ее неправильно), либо ее нет (а затем компилятор, который отклоняет ее, неверен).Это, в свою очередь, основывается на неявном допущении, что рассматриваемая ошибка, а именно отсутствие определения объекта, используемого ODR, является диагностируемой ошибкой.К сожалению это не так.Стандарт прямо заявляет, что:

[ basic.def.odr / 10 ] Каждая программа должна содержать ровно одно определение каждой не встроенной функции или переменной, используемой odrв этой программе за пределами отклоненного утверждения; Диагностика не требуется .

Поскольку данное положение стандарта проблематично и нежелательно, оно есть.Ваша программа имеет неопределенное поведение из-за отсутствия определения, и для ее диагностики не требуется реализация.Таким образом, оба компилятора технически корректны на любом уровне оптимизации.

В C ++ 17 с обязательным удалением копии программа больше не содержит ODR-использования рассматриваемой переменной. Статические члены-члены constexpr неявно встроены, не являются отдельныминужно определение (спасибо Олив).

0 голосов
/ 21 ноября 2018

Ответьте на мой собственный вопрос.

У меня есть смутное понимание о odr-use .

  • Для литерального типа Foo :: bar1 он не используется odr, поэтому он подходит.
  • Для struct Foo :: bar2: когда возвращает структуру внутри тела функции, он вызовет свой конструктор копирования, который принимает ссылку на Foo::bar2.Так что Foo::bar2 используется odr, его определение должно существовать где-то в программе, иначе это приведет к ошибке ссылки.

Но почему g ++ не жалуется?Я предполагаю, что это связано с оптимизацией компилятора.

Проверьте мои предположения:

  1. copy elision

    add -fno-elide-constructors, g++ -fno-elide-constructors -std=c++11 a.cpp

    / tmp / ccg1z4V9.o: в функции foo2()': a.cpp:(.text+0x27): undefined reference to Foo :: bar2 '

    Так что, да, разрешение копирования повлияет на это.Но g++ -O1 все еще передается.

  2. встроенная функция

    add -fno-line, g++ -O1 -fno-elide-constructors -fno-inline -std=c++11 a.cpp

    / tmp / ccH8dguG.o: В функции foo2()': a.cpp:(.text+0x4f): undefined reference to Foo :: bar2 '

Вывод: разрешение на копирование и встроенная функция влияют на его поведение.Разница между g ++ и clang заключается в том, что g ++ по умолчанию разрешает копирование, а clang - нет.

...