在通用lambda中使用`if constexpr`访问成员类型需要两个分支都是格式良好的 - gcc vs clang

Vit*_*meo 19 c++ sfinae language-lawyer c++17 if-constexpr

考虑两个struct具有不同成员类型别名的s:

struct foo { using x = int;   };
struct bar { using y = float; };
Run Code Online (Sandbox Code Playgroud)

给定Ttemplate情况下,我想无论是T::x还是T::y取决于什么T是:

template <typename T>
auto s()
{
    auto l = [](auto p) 
    {
        if constexpr(p) { return typename T::x{}; }
        else            { return typename T::y{}; }
    };

    return l(std::is_same<T, foo>{});
}

int main() 
{ 
    s<foo>(); 
}
Run Code Online (Sandbox Code Playgroud)

g++编译上面的代码,同时clang++产生这个错误:

error: no type named 'y' in 'foo'
        else            { return typename T::y{}; }
                                 ~~~~~~~~~~~~^
note: in instantiation of function template specialization 's<foo>' requested here
    s<foo>();
    ^
Run Code Online (Sandbox Code Playgroud)

在godbolt.org上,有一致性查看器


clang++不正确地拒绝这个代码?

注意,clang++通过通用lambda删除间接时接受代码l:

template <typename T>
auto s()
{
    if constexpr(std::is_same<T, foo>{}) { return typename T::x{}; }
    else                                 { return typename T::y{}; }
}
Run Code Online (Sandbox Code Playgroud)

Col*_*mbo 6

请参阅Richard Smith关于std讨论的帖子:

在我熟悉[即Clang]的实现中,一个关键问题是在处理函数定义时使用的词法范围基本上是暂时的,这意味着延迟函数模板定义的某些部分的实例化很难支持.通用lambda不会遇到问题,因为泛型lambda的主体用封闭的函数模板实例化,[...]

也就是说,在实例化模板时,使用本地上下文(包括模板参数)部分地实例化通用lambdas的实体; 从而下铛的实现,T::x并且T::y被直接取代的,因为封闭件类型可以外面通过.这导致了失败.正如@TC所指出的,代码可以被认为是不正确的,不需要诊断,因为实例化s<foo>产生模板定义(闭包的模板定义),其第二个if constexpr分支没有良好形成的实例化.这解释了Clang和GCC的行为.

这归结为一个主要实现中的架构问题(另见这个答案 ; GCC显然没有受到这个限制),所以如果Core认为你的代码格式正确(毕竟,他们占了这在通用lambda捕获的设计中 - 参见链接的答案).支持代码的GCC充其量只是一个功能(但可能有害,因为它使您能够编写依赖于实现的代码).