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?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With