含std::unique_ptr的std::variant列表初始化:std::array可编译std::vector报错原因
核心问题出在**std::unique_ptr的不可拷贝特性**,以及std::array和std::vector初始化机制的本质差异上:
1. 为什么你的varianttypes没有拷贝构造函数?
你的varianttypes是std::variant<int, double, std::unique_ptr<double>>,而std::unique_ptr是独占所有权的智能指针,它的拷贝构造函数被显式删除了。根据C++标准,当一个类包含不可拷贝的成员时,该类的拷贝构造函数会被隐式删除——这意味着你的varianttypes对象只能被移动,不能被拷贝。
2. std::array的初始化为什么能正常工作?
std::array是一个聚合类型,它的初始化属于聚合初始化。当你写:
std::array<varianttypes, 4> a{1, 2.2, 3, 4.3};
编译器会直接在std::array的每个内存位置上,用初始化列表里的对应值原位构造varianttypes对象:
- 第一个位置用
1直接构造variant<int, ...> - 第二个位置用
2.2直接构造variant<double, ...> - 以此类推
整个过程不需要拷贝或移动任何varianttypes对象,所以即使varianttypes不可拷贝,也完全没问题。
3. std::vector的初始化为什么会报错?
std::vector的初始化列表构造逻辑和std::array完全不同:
当你写std::vector<varianttypes> a{1, 2.2, 3, 4.3};时,编译器会先把初始化列表里的每个值转换成varianttypes的临时对象,然后尝试把这些临时对象拷贝到std::vector分配的内存中。
但你的varianttypes没有拷贝构造函数(被std::unique_ptr禁用),所以这一步的拷贝操作无法完成——这就是报错信息里“找不到匹配的‘construct_at’调用函数”、“调用了被隐式删除的拷贝构造函数”的原因。
解决方案
方案一:用emplace_back原位构造元素
emplace_back可以直接在std::vector的内存空间里构造varianttypes对象,完全避免拷贝或移动:
std::vector<varianttypes> a; a.emplace_back(1); // 直接构造int类型的variant a.emplace_back(2.2); // 直接构造double类型的variant a.emplace_back(3); // 直接构造int类型的variant a.emplace_back(4.3); // 直接构造double类型的variant
方案二:显式移动临时对象(C++17及以上)
虽然std::initializer_list里的元素是const的,但我们可以显式构造临时varianttypes对象并移动它们:
std::vector<varianttypes> a{ std::move(varianttypes{1}), std::move(varianttypes{2.2}), std::move(varianttypes{3}), std::move(varianttypes{4.3}) };
不过这种写法不如emplace_back简洁,更推荐第一种方案。
内容的提问来源于stack exchange,提问作者Amir

