Ответ 1
Это, скорее всего, ошибка в clang 3.2. Я не могу воспроизвести крушение с помощью створки.
Посмотрите на этот фрагмент кода:
#include <iostream>
#include <string>
void foo(int(*f)()) {
std::cout << f() << std::endl;
}
void foo(std::string(*f)()) {
std::string s = f();
std::cout << s << std::endl;
}
int main() {
auto bar = [] () -> std::string {
return std::string("bla");
};
foo(bar);
return 0;
}
Компиляция с помощью
g++ -o test test.cpp -std=c++11
приводит к:
bla
как это должно делать. Компиляция с помощью
clang++ -o test test.cpp -std=c++11 -stdlib=libc++
приводит к:
zsh: illegal hardware instruction ./test
И компиляция с помощью
clang++ -o test test.cpp -std=c++11 -stdlib=stdlibc++
приводит также к:
zsh: illegal hardware instruction ./test
Версии Clang/GCC:
clang version 3.2 (tags/RELEASE_32/final)
Target: x86_64-pc-linux-gnu
Thread model: posix
gcc version 4.7.2 (Gentoo 4.7.2-r1 p1.5, pie-0.5.5)
Какие-либо предложения о том, что происходит не так?
Спасибо заранее!
Это, скорее всего, ошибка в clang 3.2. Я не могу воспроизвести крушение с помощью створки.
Да, это ошибка в Clang++. Я могу воспроизвести его с помощью CLang 3.2 в i386-pc-linux-gnu.
А теперь некоторый случайный анализ...
Я обнаружил, что ошибка заключается в преобразовании из labmda в pointer-to-function: компилятор создает своего рода thunk с соответствующей сигнатурой, которая вызывает лямбда, но имеет инструкцию ud2
вместо ret
.
Инструкция ud2
, как вы, наверное, знаете, является инструкцией, которая явно вызывает исключение "Invalid Opcode". То есть инструкция намеренно оставила undefined.
Взгляните на дизассемблирование: это функция thunk:
main::$_0::__invoke():
pushl %ebp
movl %esp, %ebp
subl $8, %esp
movl 8(%ebp), %eax
movl %eax, (%esp)
movl %ecx, 4(%esp)
calll main::$_0::operator()() const ; this calls to the real lambda
subl $4, %esp
ud2 ; <<<-- What the...!!!
Итак, минимальный пример ошибки будет просто:
int main() {
std::string(*f)() = [] () -> std::string {
return "bla";
};
f();
return 0;
}
Любопытно, что ошибка не возникает, если тип возврата является простым типом, например int
. Тогда сгенерированный thunk:
main::$_0::__invoke():
pushl %ebp
movl %esp, %ebp
subl $8, %esp
movl %eax, (%esp)
calll main::$_0::operator()() const
addl $8, %esp
popl %ebp
ret
Я подозреваю, что проблема заключается в пересылке возвращаемого значения. Если он вписывается в регистр, например eax
, все идет хорошо. Но если это большая структура, например std::string
, она возвращается в стек, компилятор запутан и испускает ud2
в отчаянии.