std::vector<T>到QVector<T>的数据复制方案

目录

[1.Qt5.15 / Qt6 最简官方接口](#1.Qt5.15 / Qt6 最简官方接口)

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

2.1.std::make_move_iterator介绍

2.2.使用std::make_move_iterator

[3.POD 基础类型极致拷贝](#3.POD 基础类型极致拷贝)

4.通用安全深拷贝

5.C++模版实现


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

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

cpp 复制代码
#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 ,让容器批量操作时触发元素的移动语义,而非拷贝语义,从而实现高效转移数据。

它的简单实现如下:

cpp 复制代码
// 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/ 自定义结构体)。

cpp 复制代码
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 元素被移动后,处于有效但未定义状态(清空即可)。

这行代码:

cpp 复制代码
auto it = std::make_move_iterator(vec.begin());

等价于手动写模板:

cpp 复制代码
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 字符串 / 带动态内存的自定义类,会触发未定义行为!

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

cpp 复制代码
#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 动态扩容、多次重新分配内存 + 拷贝:

cpp 复制代码
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:构造函数直接传入迭代器(小数据简洁,大数据可能内部扩容)

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

5.C++模版实现

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

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

代码如下:

cpp 复制代码
#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

使用方法:

cpp 复制代码
// 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));
相关推荐
小欣加油12 小时前
leetcode3751 范围内总波动值I
java·数据结构·c++·算法·leetcode
代码中介商13 小时前
C++左值与右值:核心判断法则详解
开发语言·c++
玖玥拾13 小时前
C/C++ 基础笔记(七)
c语言·c++
珊瑚里的鱼14 小时前
手撕单例模式中的饿汉模式和懒汉模式,懒汉模式还要再多加一个C++11版本的
开发语言·c++·单例模式
zh路西法14 小时前
【Linux 串口通信】基于 C++ 多线程的同步/异步串口实现
linux·运维·c++·python
不会C语言的男孩14 小时前
C++ Primer 第12章:动态内存
开发语言·c++
thisiszdy15 小时前
<C++> 浅拷贝与深拷贝
c++
2023自学中15 小时前
Linux虚拟机 CMakeLists.txt:x86 与 ARM 双架构编译脚本
linux·c语言·c++·嵌入式
眠りたいです15 小时前
现代C++:C++17中的新库特性
开发语言·c++·c++20·c++17