extern const默认构造的对象

extern const default constructed object

本文关键字:对象 const 默认 extern      更新时间:2023-10-16

这是我的代码

#include <iostream>
using namespace std;
class Q
{
public:
Q() { cout << "constructor" << endl; }
};
extern const Q t/*()*/; //A
const Q s/*()*/; //B
int main()
{
const Q t/*()*/;
}

我希望标记为"A"的行表示我正在创建一个Q类型的对象t,它的链接是外部的,并且它的字段不能修改。创建是由我提供的没有参数的构造函数完成的。

在main中,我希望以同样的方式创建Q类型的本地t对象,尽管它的链接必然只是这个文件,而且事实上,它的范围和持续时间只针对main。

C++允许我在常量Q t/()/中放入或不放入括号;总的来说。在全局范围中,在A行中,我可以放括号,也可以不放括号,构造函数也不会被调用。

在B行中,我只允许不放括号,否则,如果我定义函数原型,编译器会感到困惑。

我的问题是:

  1. 考虑到在第//B行不存在这种灵活性,为什么允许我灵活地将()或不放在第//A行?

  2. 不管我在1.中的选择如何,我发现"构造函数"不是按A行打印的,尽管它是按B行打印的。为什么?我对此并没有什么期望。一方面,打印它是合理的,因为我们主要看到C++理解调用0参数构造函数,即使没有括号。另一方面,如果是这种情况,那么我将如何对类类型进行非定义声明?

带有extern关键字的对象声明不是定义,除非它包含显式初始值设定项。您尝试使用()来强制定义是朝着正确方向迈出的一步。然而,它失败了,因为()的这种声明会产生语法歧义,这种歧义的解决有利于函数声明,而不是对象声明。

这同样适用于你的所有声明,而不仅仅是你似乎相信的B。我不清楚你为什么声称B行"不存在这种灵活性":你可以在B行中包含(),也可以省略它。无论哪种情况,声明都是完全有效的,除了()会声明一个函数。(也就是说,这不仅仅是一种"灵活性",而是一种非常剧烈的质的变化。)

对于B行,问题实际上不存在,因为B行不使用extern关键字。如果没有extern,则无论是否使用显式初始值设定项,B行都是对象定义。在您的情况下,保持原样(没有())仍然会触发默认构造,就像您想要的那样。这同样适用于main中的本地声明。

另一方面,行A是有问题的,因为为了强制定义,你需要一个显式的初始值设定项,但()并没有做你想做的事

为了在"经典"C++98中解决这个问题,您将被迫使用

extern const Q t = Q();

语法,以确保您正在创建由默认构造函数初始化的对象定义。(从教学角度讲,它实际上是默认构造,然后是复制构造,但这是我们在C++98中不得不面对的问题)。

在现代C++(C++11及更高版本)中,您可以使用{}初始值设定项将其定义为一个定义(具有默认构造)

extern const Q t{};

extern const Q t = {};

由于显而易见的原因,基于{}的语法不会在函数声明中产生任何歧义。