是否可以在 C++20 requires 子句中初始化模板 inner class?
Is it possible to initialize the template inner class in the C++20 requires clause?
考虑以下定义模板内部的 class A
class B
:
struct A {
template<class = int>
struct B { };
};
我们可以使用下面的表达式来初始化内部B
,其中typename
是可选的:(Godbolt)
int main() {
A::template B<>();
typename A::template B<>();
}
我想用concept
检测一个类型是否有模板内部classB
:
template<class T>
concept C = requires {
typename T::template B<>();
};
static_assert(C<A>);
但是只有Clang接受了上面的代码,GCC和MSVC因为语法错误拒绝了它(Godbolt):
<source>:8:27: error: expected ';' before '(' token
8 | typename T::template B<>();
| ^
| ;
如果我删除 require
子句中的 typename
:
template<class T>
concept C = requires {
T::template B<>();
};
MSVC 接受了它,但是 Clang 和 GCC 会生成 static assertion failed
,因为他们认为表达式的格式不正确 (Godbolt):
<source>:11:15: note: because 'A' does not satisfy 'C'
static_assert(C<A>);
^
<source>:8:15: note: because 'T::template B<>()' would be invalid: 'A::B' instantiated to a class template, not a function template
T::template B<>();
^
我应该信任哪个编译器?
我对概念的理解是,如果你想检查这种情况,你必须使用复合语句。
struct A
{
template<class = int>
struct B { };
};
template<class T>
concept C = requires
{
{ typename T::template B<>() };
};
static_assert(C<A>);
我相信你不能在 simple requirement
子句中使用它,因为 T::template B<>
是一个从属名称并且需要关键字 typename
在前面,因为这个概念本身就是一个模板而你正处于一个未评估的环境中。
如果你在前面使用关键字typename
,它不再是simple requirement
而是变成type requirement
。但是这个检查类型是否存在,并不是为了构建表达式来实例化类型。
因为这样你最终得到一个复合表达式,你可以在依赖模板中使用关键字 typename
获取类型并对该类型进行默认初始化。
作为替代方案,如 C2
所示,您可以在概念模板参数列表中预先选择您的类型,这样您就可以在 [=21= 中使用没有关键字 typename
的找到的类型].
我还添加了测试用例,以通过“杀死”内部类型的默认构造函数来查看概念失败 B
。
struct A
{
template<class = int>
struct B { };
};
struct A2
{
template<class = int>
struct B { B()=delete; };
};
template<class T>
concept C = requires
{
{ typename T::template B<>() };
};
template<class T, typename Inner = typename T::template B<> >
concept C2 = requires
{
Inner();
};
static_assert(C<A>);
static_assert(C<A2>); // fails as expected
static_assert(C2<A>);
static_assert(C2<A2>); // fails as expected
以上代码适用于所有三个给定的编译器:explore
考虑以下定义模板内部的 class A
class B
:
struct A {
template<class = int>
struct B { };
};
我们可以使用下面的表达式来初始化内部B
,其中typename
是可选的:(Godbolt)
int main() {
A::template B<>();
typename A::template B<>();
}
我想用concept
检测一个类型是否有模板内部classB
:
template<class T>
concept C = requires {
typename T::template B<>();
};
static_assert(C<A>);
但是只有Clang接受了上面的代码,GCC和MSVC因为语法错误拒绝了它(Godbolt):
<source>:8:27: error: expected ';' before '(' token
8 | typename T::template B<>();
| ^
| ;
如果我删除 require
子句中的 typename
:
template<class T>
concept C = requires {
T::template B<>();
};
MSVC 接受了它,但是 Clang 和 GCC 会生成 static assertion failed
,因为他们认为表达式的格式不正确 (Godbolt):
<source>:11:15: note: because 'A' does not satisfy 'C'
static_assert(C<A>);
^
<source>:8:15: note: because 'T::template B<>()' would be invalid: 'A::B' instantiated to a class template, not a function template
T::template B<>();
^
我应该信任哪个编译器?
我对概念的理解是,如果你想检查这种情况,你必须使用复合语句。
struct A
{
template<class = int>
struct B { };
};
template<class T>
concept C = requires
{
{ typename T::template B<>() };
};
static_assert(C<A>);
我相信你不能在 simple requirement
子句中使用它,因为 T::template B<>
是一个从属名称并且需要关键字 typename
在前面,因为这个概念本身就是一个模板而你正处于一个未评估的环境中。
如果你在前面使用关键字typename
,它不再是simple requirement
而是变成type requirement
。但是这个检查类型是否存在,并不是为了构建表达式来实例化类型。
因为这样你最终得到一个复合表达式,你可以在依赖模板中使用关键字 typename
获取类型并对该类型进行默认初始化。
作为替代方案,如 C2
所示,您可以在概念模板参数列表中预先选择您的类型,这样您就可以在 [=21= 中使用没有关键字 typename
的找到的类型].
我还添加了测试用例,以通过“杀死”内部类型的默认构造函数来查看概念失败 B
。
struct A
{
template<class = int>
struct B { };
};
struct A2
{
template<class = int>
struct B { B()=delete; };
};
template<class T>
concept C = requires
{
{ typename T::template B<>() };
};
template<class T, typename Inner = typename T::template B<> >
concept C2 = requires
{
Inner();
};
static_assert(C<A>);
static_assert(C<A2>); // fails as expected
static_assert(C2<A>);
static_assert(C2<A2>); // fails as expected
以上代码适用于所有三个给定的编译器:explore