使用模板调用操作符和泛型lambdas重载结构 - gcc vs clang

Vit*_*meo 11 c++ lambda overloading language-lawyer c++17

我发现了一个代码片段,它在clang ++ 4(和trunk)中编译并正常工作,但无法在g ++ 7(和trunk)中编译.我们假设我有以下struct类型:

struct a { void foo() { } };
struct b { void bar() { } };
struct c { void bar() { } };
Run Code Online (Sandbox Code Playgroud)

我想创建一个过载设定的lambda表达式哪些处理a明确,而bc被"捕获"用一个通用的λ auto参数:

auto ol = overload([](a x)   { x.foo(); },
                   [](auto x){ x.bar(); })
Run Code Online (Sandbox Code Playgroud)

当我调用时ol(a{}):

  • 铛++编译和行为与预期:a"匹配"的第一拉姆达,而bc匹配第二个.

  • g ++无法编译,出现以下错误:

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

    似乎编译器试图实例化第二个lambda,即使第一个lambda是更好的匹配方式.希望这是一个bug,因为它对我来说似乎不直观.


请注意,如果使用一些老式实例而不是lambda表达式,两个编译器都能正常工作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!
Run Code Online (Sandbox Code Playgroud)

我期望lambda表达式是大致相当于s0s1,所以这是更令人惊讶的.


这是我生成重载集的方式:

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)...};
}
Run Code Online (Sandbox Code Playgroud)

这是一个实例gcc.godbolt.org,展示了编译器之间的不同行为.


这是一个g ++错误吗?或者标准中有什么东西使lambda struct在这种情况下的行为与实例不同?

Bar*_*rry 7

我认为这是一个gcc bug(提交为80767),与[temp.inst]/9运行冲突:

实现不应隐式实例化函数模板,变量模板,成员模板,非虚拟成员函数,成员类,类模板的静态数据成员或constexpr if语句的子语句,除非这样的实例化是必须的.

通用拉姆达的的实例operator()auto = a不是必需的,因此它不应该被实例化.

  • 该错误来自将转换函数模板实例化为指向函数的指针,这意味着这是https://gcc.gnu.org/bugzilla/show_bug.cgi?id=71117的另一个版本. (4认同)