为什么转换函数声明不需要至少一个定义类型说明符

why a conversion function declaration does not require at least one defining-type-specifier

Except in a declaration of a constructor, destructor, or conversion function, at least one defining-type-specifier that is not a cv-qualifier shall appear in a complete type-specifier-seq or a complete decl-specifier-seq.

构造函数是一个例外,因为构造函数可以声明为 constructor(){},在此声明中没有定义类型说明符,与析构函数类似。

对于 conversion function,当我在上面的引用中思考时,我不知道转换函数不需要类型说明符,defining-type-specifier 包含 type-specifier,因为句子 defining-type-specifier that is not a cv-qualifier 暗示只有 type-specifier 包含 cv-qualifier,因此我认为一个转换函数声明至少包含一个defining-type-specifier(在更小的范围内,它是type-specifier是defining-type-specifier的子集),一个转换的语法由于 [dcl.fct.def#general-1]:

,函数如下所示

attribute-specifier-seq(opt) decl-specifier-seq(opt) declarator virt-specifier-seq(opt) function-body

因此,其声明符如下所示:

operator conversion-type-id

conversion-type-id

type-specifier-seq conversion-declarator(opt)

然而根据[class.conv.fct],它说:

A decl-specifier in the decl-specifier-seq of a conversion function (if any) shall be neither a defining-type-specifier nor static.

这意味着 decl-specifier-seq(opt) 不能是定义类型说明符,也不能是静态的。

但是,在转换类型标识的类型说明符序列中,它必须是定义类型说明符。

operator Type(){  // Type is a defining-type-specifier(more exactly,it's a type-specifier)
  return Type{};
}

而且您不会像这样定义转换函数:

operator (){ // there's no defining-type-specifier in type-specifier-seq
  //...
}

[dcl.fct#11]

Types shall not be defined in return or parameter types.

这是定义类型说明符必须出现在函数声明中的限制,由于

定义类型说明符包括:

  • type-specifier
  • class-specifier
  • enum-specifier
由于最后一个引号,

class-specifierenum-specifier 不能用于函数声明的声明说明符序列。只允许 type-specifier

所以,就目前而言,标准实际上想说的是,对 type-specifier[=69= 使用 more range 措辞],即 defining-type-specifier,因为你确实可以声明一个变量,比如 struct A{} variable;,没有 class-specifier 包含在类型说明符中,因此,作为一般规则,标准使用“措辞”定义类型说明符 来涵盖此类情况。

那么,为什么转换函数在第一条规则中是例外?以上分析如有误解,请指正。

问题:

  1. 为什么转换函数在第一个引用中是异常的?
  2. 如果一个转换函数是异常的,为什么其他函数不是?

函数声明必须有一个定义类型说明符只是意味着函数声明必须具有以下形式:

   Type  f();
// ^^^^ defining-type-specifier (in this case, a type-specifier)
// this must be an existing type

并且不能是以下形式:

f(); // error, no defining-type-specifier

引用自dcl.fct的规则:

Types shall not be defined in return or parameter types.

defining-type-specifiers 无关(尽管术语相似)。它只是意味着您不能在函数声明中定义类型。

struct A{} f(); // error, can't define a type in return
void f(struct A{}); // error, can't define a type in parameter

因此这与您问题开头引用的例外情况不冲突。

我同意 - [dcl.type]/3 段应该这样说:

Except in a declaration of a constructor, destructor, or conversion function, or in a lambda-declarator, a complete decl-specifier-seq shall contain at least one defining-type-specifier that is not a cv-qualifier. A complete type-specifier-seq shall contain at least one type-specifier that is not a cv-qualifier.

你说得对:

  • defining-type-specifier 解析比 type-specifier.
  • 更广泛的一组输入标记序列
  • decl-type-specifierdefining-type-specifier.[=137= 解析更广泛的输入标记序列集]
  • constvolatile 在解析这三个中的任何一个时都有效。
  • class ClassName {...};enum EnumName {...}; 语法在 defining-type-specifierdecl-type-specifier 但不在 type-specifier.

C++ 语法 使用 type-specifier-seqdecl-specifier-seq在许多需要类型名称的地方(加上一些不命名类型的地方)。在这些序列中,引用的段落 [dcl.type]/3 要求“至少一个 defining-type-specifier 不是 cv-qualifier”主要是说在所有这些上下文中,根本不命名类型的变体是不允许的:你不能说 auto v1 = new const;static constexpr typedef f();。大多数个人用途对 type-specifier 可以出现和不可以出现的种类有额外的限制,但这些规则是对这个基本规则的补充。特别是,它们中的许多不允许在说明符序列中定义类型。但是由于 decl-type-specifiersimple-declaration 中被用作定义 classes 和枚举的普通方式,这条规则不是该限制的地方。

排除构造函数、析构函数和转换函数的原因是,尽管它们可能根本没有 decl-type-specifier-seq,但实际上它们可能使用decl-type-specifier 不包含任何 defining-type-specifier。例如,在

class MyClass {
public:
  explicit MyClass(int);
};

构造函数声明有一个 decl-type-specifier 其唯一的说明符是 explicit 关键字,它不是 defining-type-说明符.

(但是,在浏览过程中,我发现了另一个不应应用该规则的上下文:lambda 表达式允许在其参数列表之后使用可选的 decl-specifier-seq,其中唯一允许的说明符是 mutableconstexprdefining-type-specifier.)

也不是

我猜这个段落版本伴随或遵循 C++14 和 C++17 之间的语法变化。 decl-specifier-seqsimple-declaration 中的初始 decl-specifier-seq 从可选更改为必需。添加了一个新的语法符号 nodeclspec-function-declaration 以涵盖友元声明和模板相关声明的情况,这些声明声明构造函数、析构函数或转换函数,没有初始说明符且没有定义它们。 function-definitionmember-declaration 实际上涵盖了构造函数、析构函数和转换函数的其他声明,它们仍然使用可选的 decl-specifier-seq,因此对 simple-declaration 的更改不会影响它们。

对于转换函数,[class.conv.fct]/1中的文字说

A decl-specifier in the decl-specifier-seq of a conversion function (if any) shall be neither a defining-type-specifier nor static.

形成实际要求:[dcl.type] 语句从其通常要求中排除了转换函数的 decl-type-specifier-seq,因此它不会说什么是合法的,什么是不合法的。 [class.conv.fct] 这句话给出了这种情况的实际规则。

可以声明一个转换函数:

  • 函数定义(如果它有一个主体,包括=default;=delete;
  • 通过成员声明(在class定义内,如果声明没有主体)
  • 通过简单声明nodeclspec-function-declaration(如果在友元声明、显式特化或显式实例化中)

A nodeclspec-function-declaration 不允许初始说明符,但其他三个符号都有一个规则,其中 decl-specifier-seq(必需或可选)后跟一个 声明符 init-declarator-list。如您所述,转换函数的 声明符 包含 operator 关键字,后跟 type-specifier-seq声明符 还必须包含 ()(void) 或等效项,以便它声明一个不带参数的函数。

再做一些假设,转换函数声明的一般形式是

attribute-specifier-seqopt decl-specifier-seqopt operator type-specifier-seq conversion-declaratoropt attribute-specifier-seqopt parameters-and-qualifiers virt-specifier-seqopt pure-specifieropt ;

attribute-specifier-seqopt decl-specifier-seqopt operator type-specifier-seq conversion-declaratoropt attribute-specifier-seqopt parameters-and-qualifiers virt-specifier-seqopt function-body

所以在 operator 关键字之前有一个可选的 decl-specifier-seq 和一个必需的 type-specifier-seq在它之后。它是 decl-specifier-seq 可能根本不存在,并且不能包含 defining-type-specifier (因为你不' 在 operator 关键字之前放置类型)或 static(因为转换函数必须始终是非静态成员)。但是 decl-specifier-seq 可能包含 constexprinlinevirtualexplicit,或它们的组合。

您注意到的问题是 [dcl.type]/3 的措辞也意味着它在技术上不适用于 type-specifier-seq在这样的声明中,它命名了转换的目标类型。 ([dcl.pre]/4 清除了声明中关于语法符号的许多类似语句,但不适用于这种情况,因为不涉及干预范围。)我们仍然可以推断出 defining-type-specifier标准中的短语需要 ,例如“conversion-type-id 指定的类型”。但是,如果 [dcl.type]/3 中的规则适用于此 type-specifier-seq 就像它对大多数规则所做的那样会更好。