Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overloading structs with template call operator and generic lambdas - gcc vs clang

I have discovered a code snippet that compiles and works properly in clang++ 4 (and trunk) but fails to compile in g++ 7 (and trunk). Let's assume I have the following struct types:

struct a { void foo() { } };
struct b { void bar() { } };
struct c { void bar() { } };

I want to create an overload set out of lambdas which handles a explicitly, while b and c are "caught" with a generic lambda using an auto parameter:

auto ol = overload([](a x)   { x.foo(); },
                   [](auto x){ x.bar(); })

When I invoke ol(a{}):

  • clang++ compiles and behaves as expected: a "matches" the first lambda, while b and c match the second one.

  • g++ fails to compile, with the following error:

    error: 'struct a' has no member named 'bar'
               [](auto x){ x.bar(); };
                           ~~^~~
    

    It seems that the compiler tries to instantiate the second lambda even though the first one is a way better match. Hopefully this is a bug, as it seems unintuitive to me.


Note both compilers work properly if instead of lambda expressions I use some old-fashioned struct instances:

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!

I would expect the lambdas to be roughly equivalent to s0 and s1, so this is even more surprising.


This is the way I'm producing the overload set:

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)...};
}

And here's a live example on gcc.godbolt.org, showing the different behavior between the compilers.


Is this a g++ bug? Or is there something in the standard that makes lambdas behave differently from struct instances in this situation?

like image 601
Vittorio Romeo Avatar asked May 15 '17 15:05

Vittorio Romeo


1 Answers

I think this is a gcc bug (submitted as 80767), running afoul of [temp.inst]/9:

An implementation shall not implicitly instantiate a function template, a variable template, a member template, a non-virtual member function, a member class, a static data member of a class template, or a substatement of a constexpr if statement, unless such instantiation is required.

The instantiation of the generic lambda's operator() with auto = a is not required, hence it should not be instantiated.

like image 137
Barry Avatar answered Nov 03 '22 02:11

Barry