直接初始化调用所有可行构造函数(含explicit),按重载解析选最佳匹配;拷贝初始化仅考虑隐式转换构造函数,跳过explicit构造函数。

直接初始化调用哪个构造函数?
直接初始化(T obj(args) 或 T obj{args})会尝试匹配所有可行的构造函数,包括 T(const T&)、T(T&&)、T(ParamType) 等,**不跳过 explicit 构造函数**。
它按重载解析规则选择最佳匹配,优先选参数完全匹配或可隐式转换的非 explicit 构造函数;若只有 explicit 构造函数匹配(如单参且标记为 explicit),则直接初始化仍可成功,而拷贝初始化会失败。
-
std::string s1("hello")→ 调用string(const char*) -
std::vector<int> v1(10, 42)</int>→ 调用vector(size_type, const value_type&) -
std::unique_ptr<int> p1(new int(5))</int>→ 调用explicit unique_ptr(pointer),合法
拷贝初始化为什么可能跳过某些构造函数?
拷贝初始化(T obj = args;)语义上分两步:先用 args 构造一个临时对象,再用拷贝/移动构造函数初始化 obj。但编译器通常会优化掉临时对象(复制省略),**逻辑上仍受限制**:它只考虑可用于“隐式转换”的构造函数,即**忽略所有 explicit 构造函数**。
-
std::string s2 = "hello"→ 先构造临时string,再拷贝/移动;合法 -
std::unique_ptr<int> p2 = new int(5)</int>→ 错误!unique_ptr(pointer)是explicit,不可用于隐式转换 -
std::vector<int> v2 = {1,2,3}</int>→ 调用initializer_list构造函数(若未标记explicit)
初始化列表 {} 是直接还是拷贝初始化?
T obj{args} 是直接初始化(C++11 起),但它有额外约束:禁止窄化转换(narrowing conversion),且对 initializer_list 构造函数有最高优先级。
-
int x{3.14}→ 编译错误(窄化) -
std::vector<int> v{1,2,3}</int>→ 优先调用vector(initializer_list<int>)</int>,而非vector(size_type, value_type) -
MyClass m{42}→ 若存在MyClass(initializer_list<int>)</int>,即使参数个数不匹配,也可能被选中(需注意歧义)
容易踩的坑:移动构造函数没被调用?
拷贝初始化理论上应优先尝试移动构造函数(当源是右值时),但前提是移动构造函数未被定义为 delete 或不可访问,且类未禁用移动(如声明了自定义拷贝构造但未声明移动构造,C++11 后移动会被隐式删除)。
- 若你写了自定义拷贝构造函数但没写移动构造函数,
T a; T b = std::move(a);可能退化为拷贝,而非移动 - 返回局部对象时(
return local_obj;),即使拷贝构造函数被 delete,也可能触发复制省略(C++17 强制),不调用任何构造函数 -
std::vector等标准容器在拷贝初始化时若源是右值,基本都会走移动路径——但前提是你的类型满足移动条件
explicit 构造函数的存在会直接让拷贝初始化失效,而直接初始化照常工作。C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











