Неопределенная ссылка с clang++ с O2

Я пробую CLang 3.4 и libc++ в проекте и получаю странные ошибки связывания в режиме выпуска:

/home/wichtounet/dev/eddic/src/ast/Operator.cpp:17: error: undefined reference to
'std::__1::basic_ostream<char, std::__1::char_traits<char>>&
 std::__1::operator<< <char, std::__1::char_traits<char>, std::__1::allocator<char>>(
   std::__1::basic_ostream<char, std::__1::char_traits<char>>&,
   std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> const&
 )'
clang: error: linker command failed with exit code 1 (use -v to see invocation)

Все нормально компилируется. Программа правильно связывается в режиме отладки, но не работает, когда я использую O2. В O0, O1 и Os все работает нормально, а в O2, O3, Os не линкуется. Я также пробовал в режиме LTO, и он отлично работает.

Я пробовал две версии libc++, но безрезультатно.

Код мне не кажется плохим:

std::ostream& ast::operator<< (std::ostream& stream, ast::Operator op){
    std::string value = "asd";
    return stream << value;
}

но у меня нет простого примера, вызывающего проблему.

clang++ используется как для сборки, так и для компоновки кода. Я использовал "-std=c++1y -stdlib=libc++" для компиляции и то же самое плюс некоторые библиотечные материалы для компоновки.

Что может быть причиной этого?

РЕДАКТИРОВАТЬ: Полный вызов шага ссылки в режиме выпуска:

clang++ -v -use-gold -Iinclude -std=c++1y -stdlib=libc++ -Wextra -Wall -Qunused-arguments -Wuninitialized -Wsometimes-uninitialized -Wno-long-long -Winit-self -Wdocumentation -pedantic -isystem /home/wichtounet/build/modular-boost//include -L /home/wichtounet/build/modular-boost//lib -lboost_program_options -g -DLOGGING_DISABLE -DNDEBUG -O3 -march=native -fvectorize -fslp-vectorize-aggressive -fomit-frame-pointer -o release/bin/eddic "TONS OF DOT O"
clang version 3.4 (tags/RELEASE_34/final)
Target: x86_64-pc-linux-gnu
Thread model: posix
Selected GCC installation: /usr/lib/gcc/x86_64-pc-linux-gnu/4.8.2
 "/usr/bin/x86_64-pc-linux-gnu-ld" --eh-frame-hdr -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o release/bin/eddic /usr/lib/gcc/x86_64-pc-linux-gnu/4.8.2/../../../../lib64/crt1.o /usr/lib/gcc/x86_64-pc-linux-gnu/4.8.2/../../../../lib64/crti.o /usr/lib/gcc/x86_64-pc-linux-gnu/4.8.2/crtbegin.o -L/home/wichtounet/build/modular-boost//lib -L/usr/lib/gcc/x86_64-pc-linux-gnu/4.8.2 -L/usr/lib/gcc/x86_64-pc-linux-gnu/4.8.2/../../../../lib64 -L/lib/../lib64 -L/usr/lib/../lib64 -L/usr/lib/gcc/x86_64-pc-linux-gnu/4.8.2/../../../../x86_64-pc-linux-gnu/lib -L/usr/lib/gcc/x86_64-pc-linux-gnu/4.8.2/../../.. -L/lib -L/usr/lib -lboost_program_options "TONS OF DOT O" -lc++ -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc /usr/lib/gcc/x86_64-pc-linux-gnu/4.8.2/crtend.o /usr/lib/gcc/x86_64-pc-linux-gnu/4.8.2/../../../../lib64/crtn.o
src/ast/Operator.cpp:15: error: undefined reference to 'std::__1::basic_ostream<char, std::__1::char_traits<char> >& std::__1::operator<< <char, std::__1::char_traits<char>, std::__1::allocator<char> >(std::__1::basic_ostream<char, std::__1::char_traits<char> >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)'
clang: error: linker command failed with exit code 1 (use -v to see invocation)

person Baptiste Wicht    schedule 09.04.2014    source источник
comment
Я могу представить, как системные заголовки изменяют, какие другие системные заголовки они включают в зависимости от сборки отладки и сборки выпуска. Вы #include <string> или полагаетесь на какой-то другой заголовок, который иногда включает его для вас?   -  person Casey    schedule 10.04.2014
comment
Да, я включаю ‹строку› в файл, в котором, похоже, проблема.   -  person Baptiste Wicht    schedule 10.04.2014
comment
Еще один, возможно, глупый вопрос: я вижу, что в сообщении об ошибке говорится clang, а не clang++, если ваша команда ссылки на самом деле использует clang вместо clang++, она не будет включать стандартную библиотеку c++ в процесс компоновки.   -  person Casey    schedule 10.04.2014
comment
@Casey, невыполнение #include <string> обычно приводит к ошибке времени компиляции, а не к ошибке ссылки. Однако использование clang вместо clang++, скорее всего, вызовет проблемы со ссылками.   -  person Adam H. Peterson    schedule 10.04.2014
comment
Ссылка делается с помощью /usr/bin/ld. Вместо этого я попытался использовать llvm-link, но, похоже, ничего не изменилось. Кстати, я использую CMake.   -  person Baptiste Wicht    schedule 10.04.2014
comment
Это не то, о чем спрашивали. Вопрос был в том, использовали ли вы clang или clang++. clang компилирует C++ просто отлично, но он не связывается со стандартной библиотекой C++, как это делает clang++. Это своего рода ошибка, которую вы получаете, когда это происходит.   -  person Cubic    schedule 10.04.2014
comment
Я, конечно, использую Clang++. Он отлично работает в режиме отладки. Это вообще не сработает при отладке с clang. Он выходит из строя только в режиме выпуска (O2 или O3).   -  person Baptiste Wicht    schedule 11.04.2014
comment
Я думаю, что /usr/bin/ld неправильно. Он не будет автоматически добавлять библиотеки времени выполнения C++. Попробуйте скомпоновать с помощью clang++ (который способен как компилировать, так и линковать). Если вы используете /usr/bin/ld, вам нужно будет самостоятельно добавить стандартные библиотеки C++ в строку ссылок, и если вы делаете это сейчас, возможно, вы добавляете только отладочную версию библиотек (которая не обязательно будет иметь те же символы, что и библиотеки ссылок, что может привести к проблемам с компоновкой).   -  person Adam H. Peterson    schedule 11.04.2014
comment
Сейчас я связываюсь с clang++, но без разницы. Фаза компиляции и компоновки выполняется непосредственно с clang++. Он отлично работает в O0 и O1, но с O2 больше не работает (то же самое с O3 и Ofast).   -  person Baptiste Wicht    schedule 12.04.2014
comment
Просто мозговой штурм. Должен ли этот материал компилироваться с помощью -std=c++11? Если да, то что происходит на уровне оптимизации проблемы?   -  person Mike Kinghan    schedule 13.04.2014
comment
Вы должны попробовать флаг -v для вывода разных командных строк компиляции и компоновки и сравнить их между отладкой и выпуском и, например, найти разницу в путях.   -  person galop1n    schedule 13.04.2014
comment
@galop1n, @Baptiste Wicht, не только -v, но и -save-temps и сравнивать временные файлы.. Вы можете повторить команду компоновщика ld с добавленной опцией --no-demangle; тогда вы сможете найти временные файлы для проблемной функции. (ps, пожалуйста, проверьте, чистите ли вы проект между экспериментами)   -  person osgx    schedule 14.04.2014
comment
@ galop1n Я добавил вывод -v. И да, этот материал определенно должен работать с С++ 11, и код, используемый для работы на G++ и Clang 3.1 (я никогда не пробовал с libС++ в то время). Я также пробовал в режиме LTO, и он отлично работает.   -  person Baptiste Wicht    schedule 15.04.2014
comment
Я заметил, что ваш оператор‹‹ берет объект, а не константную ссылку. У вас есть auto_ptr внутри объекта ast::Operator?   -  person Alessandro Teruzzi    schedule 16.04.2014
comment
ast::Operator — это перечисление класса. Более того, я не использовал параметр param в коде.   -  person Baptiste Wicht    schedule 16.04.2014
comment
Что происходит, когда вы явно явно используете стандартную реализацию libc++ с -nostdinc++ -Ipath/to/libcxx/include -Lpath/to/libcxx/lib ?   -  person mockinterface    schedule 16.04.2014
comment
Прочитав все эти комментарии, я думаю, что было бы полезно, если бы @BaptisteWicht мог загрузить небольшой набор кодов, которые могут воспроизвести проблему, иначе все догадываются :)   -  person Jason L.    schedule 03.06.2015
comment
@ Джейсон Я бы хотел, но я никогда не мог это изолировать. Решение заключалось в том, чтобы встроить функцию в заголовок, и это сработало, но я до сих пор не знаю причину.   -  person Baptiste Wicht    schedule 03.06.2015


Ответы (2)


У меня была эта проблема с кодом, который давно скомпилирован с помощью g++, пытаясь скомпилировать с clang++. Те же симптомы: неопределенные ссылки на std::__1::basic_ostream‹...>, которые волшебным образом исчезли, когда -O2 был удален из параметров компилятора. Все подобные отчеты, которые я находил, касались ошибочного использования clang вместо clang++ для компиляции кода C++, но в моем случае это было не так: я использовал clang++.

Наконец-то я смог определить, что проблема (для меня) заключалась в использовании #include <iosfwd> (который имеет только предварительные объявления iostream для сокращения времени компиляции) в моем заголовочном (.h) файле, объявляющем класс, но не использующем #include <iostream> в соответствующем исходном файле (. cpp) файл, реализующий класс.

Как только я добавил #include <iostream> в исходный файл и перекомпилировал, эти неопределенные ошибки ссылок исчезли.

person Michael Kirkham    schedule 24.01.2017
comment
Таким образом, одна TU видит только объявление шаблона, предполагает, что создание экземпляра было выполнено где-то еще, и требует его. Другой TU имеет определение, но фактически выдает функцию только на уровнях оптимизации, где вызов не встроен. Наконец, LTO позволяет встраивать везде, снова скрывая проблему. Я думаю, ты на месте. - person Quentin; 25.01.2017

Я замечаю в сообщении об ошибке "std::__1::operator‹‹". В коде cpp я вижу "ast::operator‹‹". Это создает впечатление, что с пространствами имен что-то не так.

Теперь я не знаю, является ли ast классом или пространством имен, но в обоих случаях это выглядит немного не так.

Если ast — это класс, то я не уверен, разрешен ли оператор‹‹ в качестве члена класса. См.: http://www.cs.nctu.edu.tw/cis/chinese/doc/research/c++/C++FAQ-English/input-output.html#faq-15.8. Это предполагает, что оператор‹‹ должен быть реализован как друг, а не как член класса. Вы можете попробовать сделать его методом друга и посмотреть, решит ли это проблему.

Если ast является пространством имен, я бы попытался удалить «asd::» и добавить отдельные объявления пространства имен вокруг определения.

person Johan    schedule 16.04.2014
comment
Это пространство имен. Я попытался поместить его в глобальное пространство имен, но это ничего не изменило. - person Baptiste Wicht; 17.04.2014
comment
libc++ определяет все свои функции/классы во встроенном пространстве имен внутри std с именем __1. Это сделано, чтобы учесть будущие расширения. Я не думаю, что __1 здесь проблема. - person Marshall Clow; 29.04.2014