我是C++初学者,想了解原因
return std::list<int>();
Run Code Online (Sandbox Code Playgroud)
需要括号,但是
std::list<int> foo;
Run Code Online (Sandbox Code Playgroud)
不需要括号.这些构造函数调用之间有什么区别?
Tar*_*ama 22
这些都不是构造函数调用.
第一种是显式类型转换,它创建一个类型的对象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>
Run Code Online (Sandbox Code Playgroud)
这通常被称为最令人烦恼的解析.C++ 11引入了braced-initialization来解决这个问题:
std::list<int> foo{}; //variable definition
Run Code Online (Sandbox Code Playgroud)
(引自N3337)
"但是T()
肯定看起来像构造函数,为什么不呢?"
在该上下文中,T()
称为带功能表示法的显式类型转换:
5.2.3显式类型转换(功能表示法)[expr.type.conv]
1 [...]
2 表达式
T()
,其中T
是非数组完整对象类型的简单类型说明符或类型名称说明符,或者(可能是cv限定的)void类型,创建指定类型的prvalue,它是值初始化的(8.5 ;没有为void()情况进行初始化.[注意:如果T
是具有cv限定的非类型类型,则在确定结果prvalue(3.10)的类型时将忽略cv限定符. - 尾注]
因此,这将创建一个prvalue是值初始化.
[dcl.init]/7:
对值初始化类型的对象T
意味着:- 如果T是具有用户提供的构造函数(12.1)的(可能是cv限定的)类类型(第9节),则调用T的默认构造函数(如果T没有可访问的默认构造函数,则初始化是错误的);
- [...]
因此,这将构造函数作为值初始化的一部分进行调用,这是显式类型转换的一部分.如上所述,无法直接调用构造函数.标准说:
[class.ctor]/1:
构造函数没有名称.特殊的声明符语法用于声明或定义构造函数.语法使用:- 一个可选的decl-specifier-seq,其中每个decl-specifier都是一个函数说明符或constexpr,
- 构造函数的类名,和
- 参数列表
以该顺序.在这样的声明中,忽略构造函数类名称周围的可选括号.
因此构造函数没有名称,我们使用语言定义的语法异常声明/定义它们.
"这似乎是一个学术上的区别,这在实践中是否重要?"
也许,也许不是.我的观点是,将上述语法解释为纯构造函数调用会描绘构造函数的错误图像.构造函数初始化一个对象; 它不会分配该对象的内存,返回初始化对象,将符号绑定到该对象或由变量定义和类型转换完成的任何其他操作.此外,它可能会产生类似于OP的混淆,因为他认为这两个构造都是构造函数调用,因此他们期望统一语法.
当我们有正式条款避免混淆时,为什么要使用不精确的提示?
Dav*_*aim 10
这样看:
1)你需要创建一个对象
2)你需要返回它.
让我们说编译器查看表达式return Foo;
,编译器认为"嘿!他希望我返回一个类型!类型不是我可以返回的东西!我需要一个真正的变量!"
所以你可以写点类似的东西
Foo temp;
return temp;
Run Code Online (Sandbox Code Playgroud)
或者缩短它 - 调用默认构造函数Foo
,然后返回刚刚创建的Anonymous对象.您将构造函数视为生成对象的函数.
代码return createDefaultFoo();
看起来更合理吗?好吧,这是做什么的Foo()
,它创建并返回匿名的Foo
obejct
在这一行:
std::list<int> foo;
Run Code Online (Sandbox Code Playgroud)
编译器可以告诉你想要一个foo
从该类型命名的对象std::list<int>
.所以这()
是冗余的.在这里回答,添加()
将使编译器认为您声明了一个函数.
两个语句都调用默认构造函数.
return std::list<int>();
Run Code Online (Sandbox Code Playgroud)
这与:
std::list<int> value;
return value;
Run Code Online (Sandbox Code Playgroud)
这里创建一个对象(使用默认构造函数)并返回对象.
std::list<int> foo;
Run Code Online (Sandbox Code Playgroud)
这里foo
使用默认构造函数创建对象.
以下是其他方法C++11
:
std::list<int> foo;
std::list<int> foo1{}; // C++11
Run Code Online (Sandbox Code Playgroud)