在通用 lambda 中使用 `if constexpr` 访问成员类型需要两个分支都是格式正确的 - gcc vs clang
Accessing member type with `if constexpr` inside generic lambda requires both branches to be well-formed - gcc vs clang
考虑两个具有不同成员类型别名的 struct
:
struct foo { using x = int; };
struct bar { using y = float; };
给定 template
上下文中的 T
,我想获得 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>();
}
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>();
^
on godbolt.org, with conformance viewer
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{}; }
}
参见 Richard Smith's post 标准讨论:
In the implementation I'm familiar with [i.e. Clang], a key problem is that the lexical scopes used while processing a function definition are fundamentally transient, which means that delaying instantiation of some portion of a function template definition is hard to support. Generic lambdas don't suffer from a problem here, because the body of the generic lambda is instantiated with the enclosing function template, [..]
也就是说,在实例化模板时,使用本地上下文(包括模板参数)部分实例化了通用 lambda 的主体;因此在 Clang 的实现中,T::x
和 T::y
被直接替换,因为闭包类型可以传递到外部。这导致失败。正如@T.C 所指出的,代码可以被认为是错误的,不需要诊断,因为 s<foo>
的实例化产生了一个模板定义(闭包的定义),其第二个 if constexpr
分支没有格式正确的实例化。这解释了 Clang 和 GCC 的行为。
这归结为主要实现中的架构问题(另请参阅 ;GCC 显然不受此限制),所以如果 Core 认为您的代码很好,我会感到惊讶 -形成(毕竟,他们在通用 lambda 捕获的设计中考虑了这一点——请参阅链接的答案)。支持您的代码的 GCC 充其量只是一项功能(但可能有害,因为它使您能够编写依赖于实现的代码)。
考虑两个具有不同成员类型别名的 struct
:
struct foo { using x = int; };
struct bar { using y = float; };
给定 template
上下文中的 T
,我想获得 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>();
}
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>();
^
on godbolt.org, with conformance viewer
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{}; }
}
参见 Richard Smith's post 标准讨论:
In the implementation I'm familiar with [i.e. Clang], a key problem is that the lexical scopes used while processing a function definition are fundamentally transient, which means that delaying instantiation of some portion of a function template definition is hard to support. Generic lambdas don't suffer from a problem here, because the body of the generic lambda is instantiated with the enclosing function template, [..]
也就是说,在实例化模板时,使用本地上下文(包括模板参数)部分实例化了通用 lambda 的主体;因此在 Clang 的实现中,T::x
和 T::y
被直接替换,因为闭包类型可以传递到外部。这导致失败。正如@T.C 所指出的,代码可以被认为是错误的,不需要诊断,因为 s<foo>
的实例化产生了一个模板定义(闭包的定义),其第二个 if constexpr
分支没有格式正确的实例化。这解释了 Clang 和 GCC 的行为。
这归结为主要实现中的架构问题(另请参阅