【问题标题】:Is it possible to initialize the template inner class in the C++20 requires clause?是否可以在 C++20 requires 子句中初始化模板内部类?
【发布时间】:2022-01-21 10:48:29
【问题描述】:

考虑以下定义模板内部类B的类A

struct A {
  template<class = int>
  struct B { };
};

我们可以使用下面的表达式来初始化内部B,其中typename可选的:(Godbolt)

int main() {
  A::template B<>();
  typename A::template B<>();
}

我想用concept来检测一个类型是否有模板内部类B

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

我应该信任哪个编译器?

【问题讨论】:

  • templateA::template 中是不必要的,因为A 不依赖。您可以从要求中省略(),至少使它们在语法上有效。

标签: c++ templates language-lawyer c++20 c++-concepts


【解决方案1】:

我对概念的理解是,如果你想检查这种情况,你必须使用复合语句。

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&lt;&gt; 是一个从属名称,需要在前面加上关键字typename,因为这个概念本身就是一个模板,而你正处于未评估的上下文中.

如果你在前面使用关键字typename,它不再是simple requirement,而是变成type requirement。但是这个检查类型是否存在,并不打算构建一个表达式来实例化该类型。

因此,您最终会得到一个复合表达式,您可以在其中获取依赖模板中带有关键字 typename 的类型,并对该类型进行默认初始化。

作为替代方案,显示为C2,您可以在概念模板参数列表中预先选择您的类型,这样您就可以在simple requires clause 中使用没有关键字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

【讨论】:

    猜你喜欢
    • 2022-06-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-29
    • 1970-01-01
    • 2023-03-04
    • 1970-01-01
    • 2011-01-19
    相关资源
    最近更新 更多