Namespaces
Variants

Value-initialization

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

这是在用空初始化器构造对象时执行的初始化操作。

目录

语法

T () (1)
new T () (2)
Class :: Class ( ... ) : member () { ... } (3)
T object {}; (4) (C++11 起)
T {} (5) (C++11 起)
new T {} (6) (C++11 起)
Class :: Class ( ... ) : member {} { ... } (7) (C++11 起)

说明

值初始化在以下情况下执行:

1,5) 当使用由空圆括号对组成的初始化器创建无名临时对象时 或花括号 (since C++11)
2,6) 当具有动态存储期的对象通过 new 表达式 创建,且初始化器由一对空圆括号 或花括号 (C++11 起) 组成时;
3,7) 当使用带有空圆括号的 成员初始化器 或花括号 (since C++11) 来初始化非静态数据成员或基类时;
4) 当使用由一对花括号组成的初始化器声明具名对象(自动、静态或线程局部)时。

在所有情况下,若使用空大括号对 {} T 为聚合类型,将执行 聚合初始化 而非值初始化。

T 是类类型且没有默认构造函数,但具有接受 std::initializer_list 的构造函数,则执行 列表初始化

(C++11 起)

值初始化的效果是:

  • 如果 T 是(可能带有 cv 限定符的)类类型:
  • 否则,若 T 为数组类型,则对数组的每个元素进行值初始化。
  • 否则,该对象将进行零初始化。

注释

语法 T object ( ) ; 不会初始化对象;它声明了一个无参数且返回 T 类型的函数。在 C++11 之前,对具名变量进行值初始化的方式是 T object = T ( ) ; ,这种方式会先对临时对象进行值初始化,再通过拷贝初始化对象:大多数编译器在这种情况下会 优化掉拷贝操作

引用无法进行值初始化。

函数式转换 所述,当 T 表示数组类型时,语法 T ( ) (1) 被禁止使用,而 T { } (5) 则被允许。

所有标准容器( std::vector std::list 等)在通过单个 size_type 参数构造时,或通过调用 resize ( ) 进行扩容时,会对其元素进行值初始化,除非其分配器自定义了 construct 的行为。

示例

#include <cassert>
#include <iostream>
#include <string>
#include <vector>
struct T1
{
    int mem1;
    std::string mem2;
    virtual void foo() {} // 确保 T1 不是聚合类型
}; // 隐式默认构造函数
struct T2
{
    int mem1;
    std::string mem2;
    T2(const T2&) {} // 用户提供的拷贝构造函数
};                   // 无默认构造函数
struct T3
{
    int mem1;
    std::string mem2;
    T3() {} // 用户提供的默认构造函数
};
std::string s{}; // 类类型 => 默认初始化,值为 ""
int main()
{
    int n{};                // 标量类型 => 零初始化,值为 0
    assert(n == 0);
    double f = double();    // 标量类型 => 零初始化,值为 0.0
    assert(f == 0.0);
    int* a = new int[10](); // 数组 => 每个元素的值初始化
    assert(a[9] == 0);      //          每个元素的值为 0
    T1 t1{};                // 具有隐式默认构造函数的类 =>
    assert(t1.mem1 == 0);   //     t1.mem1 被零初始化,值为 0
    assert(t1.mem2 == "");  //     t1.mem2 被默认初始化,值为 ""
//  T2 t2{};                // 错误:没有默认构造函数的类
    T3 t3{};                // 具有用户提供默认构造函数的类 =>
    std::cout << t3.mem1;   //     t3.mem1 被默认初始化为不确定值
    assert(t3.mem2 == "");  //     t3.mem2 被默认初始化,值为 ""
    std::vector<int> v(3);  // 每个元素的值初始化
    assert(v[2] == 0);      // 每个元素的值为 0
    std::cout << '\n';
    delete[] a;
}

可能的输出:

42

缺陷报告

以下行为变更缺陷报告被追溯应用于先前发布的C++标准。

缺陷报告 适用标准 发布时行为 正确行为
CWG 178 C++98 不存在值初始化;空初始化器调用默认
初始化(尽管 new T ( ) 也会执行零初始化)
空初始化器调用
值初始化
CWG 543 C++98 对于没有任何用户提供构造函数的类对象,
值初始化等同于对每个子对象进行值初始化
(这不需要对具有用户提供默认构造函数的成员进行零初始化)
先零初始化整个对象,
再调用默认构造函数
CWG 1301 C++11 对具有被删除默认构造函数的联合体进行值初始化会导致零初始化 改为执行
默认初始化
CWG 1368 C++98 任何用户提供的构造函数都会跳过零初始化 仅用户提供的
默认构造函数会跳过零初始化
CWG 1502 C++11 对没有用户提供默认构造函数的联合体进行值初始化
仅执行零初始化,忽略默认成员初始化器
在零初始化后
执行默认初始化
CWG 1507 C++98 对于没有任何用户提供构造函数的类对象,
值初始化不会检查平凡默认构造函数的有效性
会检查平凡
默认构造函数的有效性
CWG 2820 C++98 零初始化后的默认初始化要求非平凡构造函数 不再要求
CWG 2859 C++98 类对象的值初始化可能涉及零初始化,
即使默认初始化实际上未选择用户提供的构造函数
此情况下不进行
零初始化

参见