Перегрузка структур с помощью оператора вызова шаблона и общего lambdas - gcc vs clang

Я обнаружил фрагмент кода, который компилируется и работает правильно в 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 в этой ситуации?

Ответ 1

Я думаю, что это gcc-ошибка (представленная как 80767), работающая от [temp.inst]/9:

Реализация не должна имплицитно создавать шаблон функции, шаблон переменной, шаблон-член, не виртуальную функцию-член, класс-член, статический элемент данных шаблона класса или подстановку оператора constexpr if, если это не требуется.

Создание экземпляра общей лямбда operator() с auto = a не требуется, поэтому его не следует создавать.