std::vector<T>到QVector<T>的数据复制方案
2026/6/6 8:46:04 网站建设 项目流程

目录

1.Qt5.15 / Qt6 最简官方接口

2.不需要保留源 std::vector:移动语义(性能最优,无元素拷贝,仅转移资源)

2.1.std::make_move_iterator介绍

2.2.使用std::make_move_iterator

3.POD 基础类型极致拷贝

4.通用安全深拷贝

5.C++模版实现


1.Qt5.15 / Qt6 最简官方接口

Qt 官方封装优化,内部预分配内存,性能等价手动reserve+append,一行代码:

#include <QVector> #include <vector> std::vector<int> src{1,2,3,4,5}; // 深拷贝生成QVector QVector<int> dst = QVector<int>::fromStdVector(src); //方向 // Qt5.15+ std::vector<int> vec = qvec.toStdVector();

适用:所有类型,跨平台安全,项目 Qt 版本达标首选

Qt5.14 及更早无fromStdVector,使用下面手动方案。

2.不需要保留源 std::vector:移动语义(性能最优,无元素拷贝,仅转移资源)

2.1.std::make_move_iterator介绍

std::make_move_iterator是 C++11 引入的迭代器工厂函数,核心作用是:将普通迭代器包装成「移动迭代器」std::move_iterator,让容器批量操作时触发元素的移动语义,而非拷贝语义,从而实现高效转移数据。

它的简单实现如下:

// GCC 标准库 <iterator> 源码(精简后) template<typename _Iterator> class move_iterator { private: _Iterator _M_current; // 原始迭代器 public: using iterator_type = _Iterator; using value_type = typename iterator_traits<_Iterator>::value_type; using reference = value_type&&; // 关键:定义引用类型为 右值引用! using iterator_category = typename iterator_traits<_Iterator>::iterator_category; // 构造 explicit move_iterator(iterator_type __x) : _M_current(std::move(__x)) {} // 核心解引用:返回右值引用 reference operator*() const { return static_cast<reference>(*_M_current); // 等价 std::move(*_M_current) } // 递增迭代器 move_iterator& operator++() { ++_M_current; return *this; } }; // 工厂函数 template<typename _Iterator> inline move_iterator<_Iterator> make_move_iterator(_Iterator __it) { return move_iterator<_Iterator>(std::move(__it)); }

为什么它能实现「高效移动」?对比普通迭代器移动迭代器解引用行为,这是一切的关键:

迭代器类型解引用*it返回值容器操作时触发性能表现
普通迭代器T&(左值引用)拷贝构造 / 赋值大数据深拷贝,效率低
移动迭代器T&&(右值引用)移动构造 / 赋值仅转移资源,无内存拷贝

2.2.使用std::make_move_iterator

使用std::make_move_iterator移动元素,源 vector 内元素变为空 / 失效,容器本身内存保留,大数据首选,通用所有自定义类型(std::string/ 自定义结构体)。

template<typename T> QVector<T> vecMoveToQvec(std::vector<T>&& src) { QVector<T> dst; dst.reserve(src.size()); // 提前预分配,杜绝容器多次扩容 // 移动迭代器,转移每个对象内部资源,不复制实体数据 dst.append(std::make_move_iterator(src.begin()), std::make_move_iterator(src.end())); src.clear(); // 源容器清空 return dst; } // 调用示例 std::vector<std::string> vec{"aaa","bbb"}; auto qvec = vecMoveToQvec(std::move(vec));
  • make_move_iteratorvector的普通迭代器包装成move_iterator
  • QVector::append遍历迭代器时,调用*it得到右值引用
  • 触发元素的移动构造函数(比如QString/std::string仅转移内部指针,不拷贝字符串数据);
  • vector元素被移动后,处于有效但未定义状态(清空即可)。

这行代码:

auto it = std::make_move_iterator(vec.begin());

等价于手动写模板:

std::move_iterator<decltype(vec.begin())> it(vec.begin());

工厂函数只是为了简化代码。

3.POD 基础类型极致拷贝

关于POD类型可参考:

C++之std::is_pod(平凡的数据)

仅适用于 POD 类型(std::is_trivial_v<T>==true):内置数值、纯 C 结构体;禁止 QString/STL 字符串 / 带动态内存的自定义类,会触发未定义行为!

利用连续内存特性,整块内存复制,比循环拷贝快数倍:

#include <cstring> #include <type_traits> template<typename T> QVector<T> fastPodCopy(const std::vector<T>& src) { if(src.empty()) return {}; QVector<T> dst(src.size()); // QVector直接分配对应大小内存 // 整块内存拷贝 std::memcpy(dst.data(), src.data(), src.size() * sizeof(T)); return dst; } // 调用 std::vector<double> v(1000000, 3.14); auto qv = fastPodCopy(v);

C++20 可加约束requires std::is_trivial_v<T>限制入参。

4.通用安全深拷贝

方案 1:预分配 reserve + append 迭代器(大数据最优通用方案)

提前预留容量,避免 QVector 动态扩容、多次重新分配内存 + 拷贝:

template<typename T> QVector<T> stdVecToQVec(const std::vector<T>& src) { QVector<T> dst; dst.reserve(src.size()); // 关键:预先申请内存 dst.append(src.cbegin(), src.cend()); return dst; }

方案 2:构造函数直接传入迭代器(小数据简洁,大数据可能内部扩容)

std::vector<QString> vec{"A","B","C"}; QVector<QString> qvec(vec.begin(), vec.end());

5.C++模版实现

深拷贝(左值)+ 移动语义(右值)合并为单个通用模板函数,利用C++17 转发引用 + 完美转发自动识别:

  • 传入左值(普通 vector):自动走最优深拷贝(POD 用 memcpy,非 POD 安全拷贝)
  • 传入右值(std::move 包裹):自动走移动语义(零拷贝转移元素)
  • 全程编译期优化,无运行时损耗,一行调用搞定所有场景

代码如下:

#include <vector> #include <QVector> #include <cstring> #include <type_traits> #include <utility> // std::forward / std::move /** * @brief 万能转换:std::vector -> QVector * @param src 左值=深拷贝,右值(std::move)=移动语义,自动最优 * @return 高性能 QVector */ template <typename VecT> auto toQVector(VecT&& src) -> QVector<typename std::decay_t<VecT>::value_type> { // 萃取元素类型 T using T = typename std::decay_t<VecT>::value_type; QVector<T> dst; if (src.empty()) return dst; // ===================== 核心:自动区分 左值/右值 + POD类型 ===================== if constexpr (std::is_rvalue_reference_v<decltype(src)>) { // -------------- 场景1:右值引用(std::move)→ 移动语义(最高效)-------------- dst.reserve(src.size()); dst.append(std::make_move_iterator(src.begin()), std::make_move_iterator(src.end())); src.clear(); // 清空源容器 } else { // -------------- 场景2:左值引用 → 最优深拷贝 -------------- if constexpr (std::is_trivial_v<T> && std::is_standard_layout_v<T>) { // POD类型(int/double/float/纯结构体)→ memcpy 内存直拷 dst.resize(src.size()); std::memcpy(dst.data(), src.data(), src.size() * sizeof(T)); } else { // 非POD类型(QString/string/自定义类)→ 预分配+批量追加 dst.reserve(src.size()); dst.append(src.begin(), src.end()); } } return dst; }

C++之std::decay

使用方法:

// 1. 左值:深拷贝(保留原vector,double自动用memcpy) std::vector<double> vec1 = {1.1, 2.2, 3.3}; auto qv1 = toQVector(vec1); // 2. 左值:深拷贝(QString自动安全拷贝) std::vector<QString> vec2 = {"Qt", "C++"}; auto qv2 = toQVector(vec2); // 3. 右值:移动语义(丢弃原vector,最高效) std::vector<std::string> vec3 = {"Hello", "World"}; auto qv3 = toQVector(std::move(vec3));

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询