当用于初始化"new"时,()和{}总是等效吗?

use*_*011 8 c++ constructor initialization new-operator c++11

在使用new时,有一个帖子在类型名称之后处理括号或不处理括号.但是这个怎么样:

如果'Test'是一个普通的类,那么之间有什么区别:

Test* test = new Test();
// and
Test* test = new Test{};
Run Code Online (Sandbox Code Playgroud)

此外,假设Test2有一个类型参数的构造函数Value,它总是等于写:

Value v;
Test2 *test2 = new Test(v);
// and
Test2 *test2 = new Test{v};
Run Code Online (Sandbox Code Playgroud)

Pet*_*r K 10

在涉及的情境中可能存在差异std::initializer_list<>,例如:

案例1 - (){}

#include <initializer_list>
#include <iostream>
using namespace std;

struct Test2 {
    Test2(initializer_list<int> l) {}
};

int main() {
    Test2* test3 = new Test2(); // compile error: no default ctor
    Test2* test4 = new Test2{}; // calls initializer_list ctor
}
Run Code Online (Sandbox Code Playgroud)

案例2:(v){v}

struct Value {
};

struct Test3 {
    Test3(Value v) {}
    Test3(initializer_list<Value> v) {}
};

int main() {
    Value v;
    Test3* test5 = new Test3(v); // calls Test3(Value)
    Test3* test6 = new Test3{v}; // calls Test3(initializer_list<Value>)
}
Run Code Online (Sandbox Code Playgroud)

正如Meyers和其他人所说,使用STL时也存在巨大差异:

    using Vec = std::vector<int>;
    Vec* v1 = new Vec(10); // vector of size 10 holding 10 zeroes
    Vec* v2 = new Vec{10}; // vector of size 1 holding int 10
Run Code Online (Sandbox Code Playgroud)

并且它不局限于std::vector

在这种情况下,虽然没有区别(并initializer_list忽略了ctor)

#include <initializer_list>
#include <iostream>
using namespace std;

struct Test {
    Test() {}
    Test(initializer_list<int> l) {}
};

int main() {
    Test* test1 = new Test(); // calls default ctor
    Test* test2 = new Test{}; // same, calls default ctor
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下也存在众所周知的差异

void f() {
    Test test{};
    Test test2();
}
Run Code Online (Sandbox Code Playgroud)

其中test是default-initialized对象的类型Test,test2是一个函数声明.


Lig*_*ica 5

没有!

一个新的初始化可以采取以下几种形式:

new-initializer
   ( 表达式列表opt )
   括号初始化列表

和:

[C++11: 5.3.4/15]:新表达式创建类型的对象T如下初始化该对象:

  • 如果省略了new-initializer,则该对象为默认初始化(8.5);如果未执行初始化,则该对象具有不确定的值。
  • 否则,将根据8.5的初始化规则对new-initializer进行解释以进行直接初始化

和:

[C++11: 8.5/15]: 表单中发生的初始化

T x(a);
T x{a};
Run Code Online (Sandbox Code Playgroud)

以及在new表达式(5.3.4),static_cast表达式(5.2.9),功能符号类型转换(5.2.3)以及基本和成员初始值设定项(12.6.2)中,称为直接初始化

和:

[C++11: 8.5/16]:初始化程序的语义如下。[..]

  • 如果初始化程序是一个(非括号括起来的)braced-init-list,则该对象或引用将被列表初始化(8.5.4)。
  • [..]
  • 如果是初始值设定项(),则对象将被值初始化
  • [..]
  • 如果初始化是direct-initialization,或者如果是复制初始化,则源类型的cv不合格版本与目标的类相同,或者是其派生类,则考虑构造函数。列举了适用的构造函数(13.3.1.3),并通过重载分辨率(13.3)选择了最佳的构造函数。如此选择的构造函数将被调用以初始化对象,并将初始化器表达式或expression-list作为其参数。如果没有构造函数适用,或者重载解决方案不明确,则初始化格式错误。
  • [..]

因此,您会看到,在这种情况下(以及其他一些情况),两者都被定义为直接初始化的,但是进一步的规则意味着根据您使用的()还是{}初始化为空的情况,可能会发生不同的事情。

考虑到列表初始化的规则,在这里我不会复制它们,如果 T没有构造函数采用,则两个初始化器的效果基本相同std::initializer_list<>


Lin*_*gxi 5

一般的答案是否定的。使用花括号初始化列表作为初始化器将首先尝试解析为采用std::initializer_list. 举例来说:

#include <iostream>
#include <vector>
int main() {
  auto p = new std::vector<int>{1};
  auto q = new std::vector<int>(1);
  std::cout << p->at(0) << '\n';
  std::cout << q->at(0) << '\n';
}
Run Code Online (Sandbox Code Playgroud)

注意列表初始化的语义(引用自cppreference):

  • 所有将 std::initializer_list 作为唯一参数或作为第一个参数(如果其余参数具有默认值)的构造函数都会被检查,并通过重载解析与 std::initializer_list 类型的单个参数进行匹配
  • 如果前一阶段未生成匹配项,则 T 的所有构造函数都会参与针对由花括号初始化列表的元素组成的参数集的重载解析,但限制为仅允许非缩小转换。如果此阶段生成显式构造函数作为复制列表初始化的最佳匹配,则编译失败(注意,在简单复制初始化中,根本不考虑显式构造函数)