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

Difference between constructor calls with and without ( )

本文关键字:函数调用 之间 区别      更新时间:2023-10-16

我是一个c++初学者,想了解为什么

return std::list<int>();

需要括号,但是

std::list<int> foo;

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

这两个都不是构造函数调用。

第一个是显式类型转换,创建一个类型为std::list<int>的对象。

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

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

尽管你可能会看到"构造函数调用"这样的东西,但在c++中没有显式地、单一地调用构造函数的语法构造。

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


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

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

这通常被称为最麻烦的解析。c++ 11引入了大括号初始化来解决这个问题:

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

标准,对于那些倾斜的

(引自N3337)

"但T()肯定看起来像一个构造函数调用,为什么不是?"

在此上下文中,T()被称为具有函数符号的显式类型转换:

5.2.3显式类型转换(函数符号)[expr.type.conv]

1[…]

2 表达式T(),其中T是非数组完整对象类型或(可能是cv限定的)void类型的简单类型说明符或类型名称说明符,创建指定类型的prvalue,该prvalue为value-initialized (8.5;对于void()情况,没有进行初始化。[注:如果T是非类类型,则为。cv限定符,在确定结果的右值类型时忽略cv限定符(3.10)。-end note]

这就创建了一个prvalue它是value-initialized

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

- 如果T是具有用户提供的构造函数(12.1)的(可能是cv限定的)类类型(第9条),则调用T的默认构造函数(如果T没有可访问的默认构造函数,则初始化是病态的)构造函数),

— [...]

作为值初始化的一部分调用构造函数,值初始化是显式类型转换的一部分。如上所述,没有办法直接调用构造函数。标准是:

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

—可选的decl-specifier-seq,其中每个decl-specifier要么是函数说明符,要么是constexpr,

—构造函数的类名,和

-参数列表

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

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

"这似乎是一个学术上的区别,这在实践中重要吗?"

也许,也许不是。我的观点是,将上面的语法解释为纯粹的构造函数调用描绘了一个不正确的构造函数是什么。构造函数初始化对象;它不会分配那个对象的内存,返回初始化的对象,将符号绑定到那个对象或者其他任何由变量定义和类型转换完成的事情。此外,它还会造成像OP那样的混淆,OP希望语法统一,因为他认为这两个构造都是构造函数调用。

当我们有避免混淆的正式术语时,为什么要使用不精确的提喻?

这样看:
1)你需要创建一个对象
2)你需要归还它。

假设编译器看到表达式return Foo;,编译器认为"嘿!他要我返回一个类型!类型不是我可以返回的东西!我需要一个真正的变量!"

所以你可以写成

Foo temp;
return temp;

或者更简短一点——调用Foo的默认构造函数,然后返回我刚刚创建的Anonymous对象。将构造函数视为产生对象的函数。

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

std::list<int> foo;

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

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

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