19

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

struct foo { using x = int;   };
struct bar { using y = float; };

T在上下文中给定 a template,我想得到一个T::xT::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>(); 
}

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>();
    ^

在 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{}; }
}
4

1 回答 1

6

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

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

也就是说,当模板被实例化时,泛型 lambda 的主体部分是使用本地上下文(包括模板参数)实例化的;因此在 Clang 的实现下,T::x并被T::y直接替换,因为闭包类型可以传递到外部。这导致失败。正如@TC 所指出的,代码可以被认为是格式错误的,不需要诊断,因为实例化会s<foo>产生一个模板定义(闭包的定义),其第二个if constexpr分支没有格式正确的实例化。这解释了 Clang 和 GCC 的行为。

这归结为主要实现中的架构问题(另请参见此答案;GCC 显然不受此限制的影响),因此如果 Core 认为您的代码格式正确,我会感到惊讶(毕竟,它们占了这在通用 lambda 捕获的设计中 - 请参阅链接的答案)。支持您的代码的 GCC 充其量只是一个特性(但可能是有害的,因为它使您能够编写依赖于实现的代码)。

于 2017-06-30T11:16:45.497 回答