gcc 4.9 внутренняя ошибка компилятора при рекурсивном лямбда-возврате

У меня есть небольшой фрагмент кода, который прекрасно компилируется в головке репозитория clang (3.5), но не в репозитории gcc 4.9. Несмотря на то, что это похоже на ошибку gcc, перед спамом в bugzilla я хотел бы спросить вас,

  1. это допустимый код c ++ 1y (в текущем состоянии черновика) — только потому, что clang компилирует его, нет причин, чтобы он был правильным кодом, и
  2. если кто-то может воспроизвести эту ошибку.

Фрагмент кода, который компилируется и запускается с использованием clang, находится здесь:

http://coliru.stacked-crooked.com/a/acc691b9a407d6f2

Однако используя

g++-4.9 -o main main.cpp -std=c++1y

дает мне вышеупомянутую внутреннюю ошибку компилятора: http://pastebin.com/3fqV7xzC

Без длинной свалки это читает:

g ++ — 4.9 -o main main.cpp -std = c ++ 1y
main.cpp: в реализации ‘composer :: operator () (Func&&, Funcs&& …) :: [с auto: 2 = float; Func = main (int, const char *) ::; Funcs = {main (int, const char*) ::}]:
main.cpp: 33: 88: требуется отсюда

main.cpp:19:41: internal compiler error: in retrieve_specialization, at cp/pt.c:1042
return f(c(std::forward<Funcs>(fs)...)(v));
^

Для полноты картины приведем фрагмент (полный файл main.cpp).

#include <iostream>
#include <utility>

template <typename... Funcs>
struct composer;

template <>
struct composer<> {
auto operator()() {
return [&] (auto v) { return v; };
}
};

template <typename Func, typename... Funcs>
struct composer<Func, Funcs...> {
auto operator()(Func&& f, Funcs&&... fs) {
composer<Funcs...> c;
return [&] (auto v) {
return f(c(std::forward<Funcs>(fs)...)(v));
};
}
};

template <typename... Funcs>
auto compose(Funcs&&... fs) {
composer<Funcs...> c;
return c(std::forward<Funcs>(fs)...);
}int main (int argc, char const* argv[]) {
float v = 3.5f;
auto t = compose([] (auto v) { return v >= 3; }, [] (auto v) { return int(v-0.5); })(v);
std::cout << std::boolalpha << t << "\n";
auto f = compose([] (auto v) { return v > 3; }, [] (auto v) { return int(v-0.5); })(v);
std::cout << std::boolalpha << f << "\n";
}

Редактировать: Бонус! Мне вообще не нравится этот код — если у кого-то есть более хороший и, возможно, более быстрый способ сделать это, подумайте, чтобы просветить меня …

Редактировать 2 Кто-нибудь знает, как заставить coliru (или подобный сервис) использовать g ++ 4.9?

1

Решение

Ваш код не является допустимым C ++ 1y, по крайней мере, не при выполнении.

Вы захватываете переменные по ссылке, затем выходите из области видимости, в которой они определены, а затем вызываете лямбду, которая использует указанные переменные.

Теперь состояние c никогда не используется, но operator() вызов по-прежнему UB.

Точно так же, хотя ваши ссылки относятся к данным, которые переживают текущую область действия, нет гарантии, что будут захвачены исходные переменные, а не локальные ссылки. Одним из способов реализации локального захвата является захват указателя на локальный кадр стека и доступ к переменным с помощью статических смещений времени компиляции из указанного кадра стека: при выходе из кадра стека такое чтение приведет к появлению мусора. (Это уменьшит размер [&] лямбда до одного указателя, довольно хорошая оптимизация! А на некоторых машинах доступ к данным через смещения из указателя быстрый.)

В общем, не регистрируйте по ссылке (особенно по глобальной ссылке), если ваше закрытие (или его копии) переживет текущую область. Глобальная ссылка на return Заявление должно действительно генерировать предупреждения.

Исправление этого UB и ваш код выглядит корректно. В C ++ 1y вы можете захватитьmove или же —forward,

Без компилятора C ++ 1y вот пример:

#include <iostream>
auto compose() {
return [](auto&& x) { return std::forward<decltype(x)>(x); };
}

template<typename F>
auto compose( F&& f ) {
return std::forward<F>(f);
}

template<typename F1, typename F2>
auto compose( F1&& f1, F2&& f2 ) {
return [lhs = std::forward<F1>(f1), rhs = std::forward<F2>(f2)]( auto&& x ) {
return lhs( rhs( std::forward<decltype(x)>(x) ) );
};
}

template<typename F0, typename... Fs>
auto compose( F0&& f0, Fs&&... fs ) {
static_assert( sizeof...(Fs) > 1, "other overrides should have handled this case!  What went wrong?" );
return compose(
std::forward<F0>(f0),
compose(
std::forward<Fs>(fs)...
)
);
}

int main() {
auto a = compose( [](bool b){return !b;}, [](double d){ return d<3.0; } );
std::cout << a(2.0) << "," << a(3.0) << "\n";
return 0;
}

который также оказывается не лямбда-типом.

4

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]