【问题标题】:Difference between `vector<int> v;` and `vector<int> v = vector<int>();``vector<int> v;` 和 `vector<int> v = vector<int>();` 之间的区别
【发布时间】:2019-03-01 03:15:05
【问题描述】:

有什么区别

std::vector<int> v;

std::vector<int> v = std::vector<int>();

直观地说,我永远不会使用第二个版本,但我不确定是否有任何区别。在我看来,第二行只是一个默认构造函数,用于构建一个临时对象,然后由移动赋值运算符移动。

我想知道第二行是否不等同于

std::vector<int> v = *(new std::vector<int>()); 

因此导致向量本身在堆上(动态分配)。如果是这种情况,那么大多数情况下可能会首选第一行。

这些代码行有何不同?

【问题讨论】:

  • 即使有临时对象,它们也不会在堆上。

标签: c++ constructor initialization


【解决方案1】:

从 C++17 开始,没有任何区别。

有一个利基用例,其中std::vector = std::vector 初始化语法非常有用(尽管不是用于默认构造):当一个人想直接在类的定义:

struct S {
  std::vector<int> v; // Want to supply `(5, 42)` initializer here. How?
};

类内初始化器仅支持 ={} 语法,这意味着我们不能只说

struct S {
  std::vector<int> v(5, 42); // Error
};

如果我们使用

struct S {
  std::vector<int> v{ 5, 42 }; // or = { 5, 42 }
};

编译器会将其解释为值列表,而不是“count, value”对,这不是我们想要的。

所以,一种正确的方法是

struct S {
  std::vector<int> v = std::vector(5, 42);
};

【讨论】:

  • C++17 中的哪些变化意味着现在没有区别? C++14 有什么不同?
【解决方案2】:

第一个是default initialization,第二个是copy initialization;这里的效果是一样的,即通过std::vector的默认构造函数初始化对象v

对于std::vector&lt;int&gt; v = std::vector&lt;int&gt;();,在概念上它将构造一个临时的std::vector,然后用它来移动构造对象v(注意这里没有赋值)。根据copy elision(C++17 保证),直接调用默认构造函数初始化v

以下情况,编译器需要省略 类对象的复制和移动构造,即使复制/移动 构造函数和析构函数具有可观察到的副作用。这 对象被直接构造到存储中 否则被复制/移动到。复制/移动构造函数不需要 存在或可访问,因为语言规则确保没有复制/移动 操作发生,甚至在概念上:

  • 在初始化一个变量时,当初始化表达式 是与 变量类型:

    T x = T(T(f())); // only one call to default constructor of T, to initialize x
    

(在 C++17 之前,复制省略是一种优化。)

这是一种优化:即使它发生并且没有调用复制/移动(C++11 起)构造函数,它仍然必须存在且可访问(就好像根本没有发生优化一样),

顺便说一句:对于这两种情况,都不会通过 new 表达式构造具有动态存储持续时间的 std::vector 对象(包括潜在的临时对象)。

【讨论】:

    【解决方案3】:

    你问过:

    有什么区别

    std::vector<int> v;
    

    std::vector<int> v = std::vector<int>();
    

    ?

    正如其他人自 C++17 以来所说的那样,基本上没有区别。现在至于使用哪一个的偏好,我建议如下:

    • 如果您要创建一个稍后填写的空向量,那么第一个版本是更理想的候选者。
    • 如果您正在构造一个向量,其成员之一为默认值或指定计数,则应使用更高版本。
    • 请注意,以下两个是不同的构造函数:
      • std::vector&lt;int&gt; v = std::vector&lt;int&gt;();
      • 不是同一个构造函数:
      • std::vector&lt;int&gt; v = std::vector&lt;int&gt;( 3, 10 );
      • 构造函数的第二个nd 版本是一个重载的构造函数。
    • 否则,如果您从一组数字构造向量,则您有以下选项:

    这里是std::vectorconstructors 的列表。

    【讨论】:

    • “现在至于偏好”我会说偏好auto v = std::vector&lt;int&gt;();更好。
    • @Slava 视情况而定。有时你不想到处都用auto 来膨胀你的代码。在特定的地方使用它没有错,但不是所有的。如果您使用的是范围循环,那么一定要使用 auto 遍历容器。但是只是声明一个变量;我更喜欢查看容器及其类型,因此我知道容器的详细信息。另一个不适合使用 auto 的示例是使用某些 3rd 方库(例如 Eigen)时。他们对某些事情进行了懒惰的评估,但不是全部,auto 功能可能会导致 UB。
    猜你喜欢
    • 2021-08-26
    • 2021-07-30
    • 2016-05-30
    • 1970-01-01
    • 1970-01-01
    • 2022-01-13
    • 2020-07-15
    • 2021-11-07
    • 2020-12-27
    相关资源
    最近更新 更多