C++ 使用模板调用运算符和通用lambdas重载结构-gcc vs clang

C++ 使用模板调用运算符和通用lambdas重载结构-gcc vs clang,c++,lambda,overloading,language-lawyer,c++17,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() { } }; 我想创建一个lambda的重载集,它显式地处理a,而b和c使用auto参数用通用lambda“捕获”: auto ol = overload([](a x) { x.foo

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

struct a { void foo() { } };
struct b { void bar() { } };
struct c { void bar() { } };
我想创建一个lambda的重载集,它显式地处理
a
,而
b
c
使用
auto
参数用通用lambda“捕获”:

auto ol = overload([](a x)   { x.foo(); },
                   [](auto x){ x.bar(); })
当我调用
ol(a{})
时:

  • clang++按预期编译和运行:
    a
    与第一个lambda“匹配”,而
    b
    c
    与第二个lambda匹配

  • g++编译失败,出现以下错误:

    error: 'struct a' has no member named 'bar'
               [](auto x){ x.bar(); };
                           ~~^~~
    
    编译器似乎试图实例化第二个lambda,尽管第一个lambda更匹配。希望这是一个bug,因为它对我来说似乎不直观


注意:如果我使用一些老式的
struct
实例而不是lambda表达式,则这两个编译器都能正常工作:

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!
下面是一个示例,显示了编译器之间的不同行为


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

我认为这是一个gcc错误(提交为),与以下内容相冲突:

除非需要实例化,否则实现不应隐式实例化函数模板、变量模板、成员模板、非虚拟成员函数、成员类、类模板的静态数据成员或constexpr if语句的子语句


不需要使用
auto=a
实例化通用lambda的
运算符()
,因此不应实例化它

嗯,
何时使用…
进入标准?C++17?@Nirfiedman Yep,在C++17中,多亏了旁白:能够将函数指针作为
重载的参数是很好的。为此,请将
:Fs…
替换为
:some\u magic…
,其中
some\u magic
检测函数指针并将其映射到存储它的类,以及它保留的所有其他内容。(但这个重载可能是一个简化的MCVE,在这种情况下忽略它)错误来自实例化转换函数模板到函数指针,这意味着这是的另一个版本。
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)...};
}