Я обнаружил фрагмент кода, который компилируется и работает правильно в clang++ 4 (и trunk), но не удается скомпилировать в g++ 7 (и туловище). Предположим, что у меня есть следующие типы struct
:
struct a { void foo() { } };
struct b { void bar() { } };
struct c { void bar() { } };
Я хочу создать перегруженный набор из lambdas, который явно обрабатывает a
, а b
и c
"пойманы" с помощью общей лямбда, используя параметр auto
:
auto ol = overload([](a x) { x.foo(); },
[](auto x){ x.bar(); })
Когда я вызываю ol(a{})
:
-
clang++ компилируется и ведет себя как ожидалось:
a
"соответствует" первой лямбда, аb
иc
соответствует второй. -
g++ не скомпилируется со следующей ошибкой:
error: 'struct a' has no member named 'bar' [](auto x){ x.bar(); }; ~~^~~
Кажется, что компилятор пытается создать экземпляр второй лямбды, даже если первый из них лучше подходит. Надеюсь, это ошибка, поскольку для меня это кажется неинтуитивным.
Обратите внимание, что оба компилятора работают правильно, если вместо лямбда-выражений я использую несколько старомодных экземпляров struct
:
struct s0
{
auto operator()(a x) const { x.foo(); }
};
struct s1
{
template <typename T>
auto operator()(T x) const { x.bar(); }
};
auto os = overload(s0{}, s1{});
os(a{}); // OK!
Я ожидал бы, что лямбда будет примерно эквивалентна s0
и s1
, поэтому это еще более удивительно.
Именно так я создаю набор перегрузки:
template <typename... Fs>
struct overloader : Fs...
{
template <typename... FFwds>
overloader(FFwds&&... fs) : Fs{std::forward<FFwds>(fs)}...
{
}
using Fs::operator()...;
};
template <typename... Fs>
auto overload(Fs&&... fs)
{
return overloader<std::decay_t<Fs>...>{std::forward<Fs>(fs)...};
}
И здесь живой пример на gcc.godbolt.org
, демонстрирующий различное поведение между компиляторами.
Является ли это ошибкой g++? Или есть что-то в стандарте, которое заставляет lambdas вести себя иначе, чем экземпляры struct
в этой ситуации?