有一个声明 Stack<T>(); 对于类模板内有效的默认 ctor

use*_*570 3 c++ templates errata class-template

我看到这个关于 SO 的问题的答案,该问题与类模板的默认构造函数的声明相关,该声明表示由于CWG1435,以下代码不是有效的 C++ :

template <class T> class Stack {
public:
  Stack<T>(); //IS THIS VALID?
};

Run Code Online (Sandbox Code Playgroud)

而另一个答案说上面的例子是有效的C++。有两个来源可以证明上述示例有效:

  1. 注入的类名

否则,它被视为类型名称,相当于模板名称后跟 <> 中包含的类模板的模板参数

  1. CppCon 会议上, Dan Saks基本上展示了一个非常相似的例子。

因此,正如我们所看到的,两个链接的答案提出了相反的主张,我不知道哪一个是正确的。所以我的问题是这两个答案哪个是正确的。也就是说,声明Stack<T>();是否有效 C++。

PS:我问的是 C++11 及以后的现代 C++ 含义。

use*_*570 6

显示的代码片段对于 C++20 之前版本有效,但对于 C++20 及以上版本无效,如下所述。

C++20 之前的版本

来自class.ctor#1.2

1 -- 构造函数没有名字。在构造函数的声明中,声明符是以下形式的函数声明符:
ptr-declarator ( parameter-declaration-clause ) noexcept-specifieropt attribute-specifier-seqopt

其中 ptr 声明符仅由id-expression、可选的 attribute-specifier-seq 和可选的括号组成,并且 id-expression 具有以下形式之一:

1.2 -- 在属于类模板的成员规范但不是友元声明的成员声明中,id-表达式是一个类名,它命名直接封闭类模板的当前实例;或者

(引用结束)

这意味着在C++17中,我们可以使用Stack<T>();作为构造函数声明。

C++20

来自class.ctor#1.1

1 -- 构造函数由声明引入,其声明符是函数声明符 ([dcl.fct]),其形式为:
ptr-declarator ( parameter-declaration-clause ) noexcept-specifieropt attribute-specifier-seqopt

其中 ptr 声明符仅由 id-表达式、可选的属性说明符-seq 和可选的括号组成,并且 id-表达式具有以下形式之一:

1.1 -- 在属于类或类模板的成员规范但不是友元声明 ([class.friend]) 的成员声明中,id表达式是注入的类名称 ([class.friend])。 pre]) 的直接封闭实体

因此,正如我们所看到的,声明类模板的构造函数需要注入的类名(在您的示例中存在Stack,但不在您的示例中)。Stack<T>

这意味着您给定的代码对于 C++20 无效。


diff.cpp17.class#2也提到了同样的事情:

受影响的子条款:[class.ctor] 和 [class.dtor]

更改:简单模板 id 不再有效作为构造函数或析构函数的声明符 id。

理由:删除可能容易出错的冗余选项。

对原始功能的影响:有效的 C++ 2017 代码可​​能无法在此版本的 C++ 中编译。例如:

template<class T>
struct A {
  A<T>();           // error: simple-template-id not allowed for constructor
  A(int);           // OK, injected-class-name used
  ~A<T>();          // error: simple-template-id not allowed for destructor
};
Run Code Online (Sandbox Code Playgroud)


Ben*_*igt 5

您的示例有一个unqualified-id,它是template-id这在最新的措辞下是不允许的,构造函数必须通过其注入类名来引入。

您关于注入类名称模板名称后跟模板参数之间的等价性的引用在这里并不适用,因为这里的标识符不是类型名称,而是构造函数声明。

自 C++11 以来,这确实发生了变化。