使用和不使用()的构造函数调用之间的区别

时间:2015-10-12 11:02:38

标签: c++ constructor

我是C ++初学者,想了解原因

return std::list<int>();

需要括号,但

std::list<int> foo;

不需要括号。这些构造函数调用之间的区别是什么?

3 个答案:

答案 0 :(得分:22)

这些都不是构造函数调用。

第一个是explicit type conversion,它创建一个std::list<int>类型的对象。

第二个是变量定义,它创建一个std::list<int>类型的对象。

默认构造函数(不带参数的构造函数)在两种情况下都被称为创建的一部分。

虽然你可能会看到这些事情被称为&#34;构造函数调用&#34;,但是没有语法结构可以在C ++中显式地和单一地调用构造函数。

当另一个不需要括号的原因是因为它们是两个具有不同语法的独立语言结构而不是两种方式来调用构造函数。

请注意,如果在第二个示例中添加括号,则实际上是声明函数而不是定义变量:

std::list<int> foo; //variable definition
std::list<int> foo(); //function taking no args, returning a std::list<int>

这通常称为most-vexing-parse。 C ++ 11引入了支持初始化来解决这个问题:

std::list<int> foo{}; //variable definition

Standardese,对于那些如此倾向的人

(来自N3337的报价)

&#34;但是T()肯定看起来像构造函数调用,为什么不呢?&#34;

在该上下文中,T()被称为带功能表示法的显式类型转换:

  

5.2.3显式类型转换(功能表示法)[expr.type.conv]

     

1 [...]

     

2 表达式T(),其中T是非数组完整对象类型或(可能是cv限定的)void类型的simple-type-specifier或typename-specifier ,创建一个指定类型的prvalue,它是值初始化的(8.5;没有为void()情况进行初始化)。 [注意:如果T是非类类型的话   cv-qualified,在确定结果prvalue(3.10)的类型时,将忽略cv-qualifiers。 - 后注]

因此,这会创建一个 prvalue ,它是值初始化

  

[dcl.init]/7: value-initialize T类型的对象意味着:

     

- 如果T是具有用户提供的构造函数(12.1)的(可能是cv限定的)类类型(第9节),则T的默认构造函数被称为(并且初始化为如果T没有可访问的默认值,则格式错误   构造函数);

     

- [...]

因此,这将构造函数作为值初始化的一部分进行调用,这是显式类型转换的一部分。如上所述,无法直接调用构造函数。标准说:

  

[class.ctor]/1:构造函数没有名称。特殊的声明符语法用于声明或定义构造函数。   语法使用:

     

- 一个可选的decl-specifier-seq,其中每个decl-specifier都是一个函数说明符或constexpr,

     

- 构造函数的类名和

     

- 参数列表

     

按此顺序。在这样的声明中,忽略构造函数类名称周围的可选括号。

因此构造函数没有名称,我们使用语言定义的语法异常声明/定义它们。

&#34;这似乎是学术上的区别,这在实践中是否重要?&#34;

也许,也许不是。我的观点是,将上述语法解释为纯构造函数调用会描绘构造函数的错误图像。构造函数初始化一个对象;它没有分配该对象的内存,返回初始化的对象,将符号绑定到该对象或由变量定义和类型转换完成的任何其他操作。此外,它可能会产生类似于OP的混淆,因为他认为这两种结构都是构造函数调用,因此他们期望统一语法。

为什么在我们有正式条款避免混淆时使用不精确的提示?

答案 1 :(得分:10)

以这种方式看待它:
1)你需要创建一个对象
2)你需要退货。

让我们说编译器查看表达式return Foo;,编译器认为“嘿!他希望我返回一个类型!类型不是我可以返回的东西!我需要一个真正的变量!”

所以你可以写点像

Foo temp;
return temp;

或缩短它 - 调用Foo的默认构造函数,然后返回刚刚创建的 Anonymous 对象。您将构造函数视为生成对象的函数。

代码return createDefaultFoo();看起来更合理吗?好吧,这是Foo()所做的,它创建并返回匿名Foo obejct

在这一行:

std::list<int> foo;

编译器可以告诉您想要一个名为foo的对象来自std::list<int>类型。所以()是冗余的。正如这里所回答的,添加()将使编译器认为您声明了一个函数。

答案 2 :(得分:8)

两个语句都调用默认构造函数。

return std::list<int>();

这与:

相同
std::list<int> value;
return value;

这里创建一个对象(使用默认构造函数)并返回对象。

std::list<int> foo;

此处使用默认构造函数创建对象foo

以下是在C++11中执行相同操作的其他方式:

std::list<int> foo;
std::list<int> foo1{}; // C++11