这是在 C++11 中将一个 std::vector 的内容移动到另一个 std::vector 的末尾的最有效方法吗?

2023-11-22

我在想vector::insert() and std::copy()命令需要额外的分配。然而,如果我push_back()一个新创建的元素然后swap()我认为只要包含的类型不使用默认构造函数分配,这就会减少任何分配。

我的问题实际上是专门针对std::vector类型 sstd::string,但应该适用于其他包含的类型,如下所述:

template <typename T>
void appendMove(std::vector<T>& dst, std::vector<T>& src)
{
    dst.reserve(dst.size() + src.size())
    for(std::vector<T>::iterator it = src.begin(); it != src.end(); ++it)
    {
        dst.push_back(std::vector<T>());
        std::swap(dst.end()[-1], *it);
    }
}

我对么?我还遗漏了什么吗?也许有更好的方法来做到这一点?


性能免责声明:使用分析。

性能考虑:

  • push_back必须检查每次调用向量的容量是否足以插入元素。编译器不太可能足够聪明来避免循环内的检查,也就是说,对于每个循环迭代,它都必须进行检查,这也可能禁止进一步的优化。
  • 如果没有接到电话reserve前,push_back必须随时调整向量的容量,可能在循环内多次调整,这将导致移动已存储的元素。
  • swapmove:move 对移动对象的保证不太严格,这可以允许优化
  • As G曼尼克G评论中指出,vector::insert可以在插入之前保留必要的内存,因为它可以插入整个范围。这可能需要专门研究随机访问迭代器,因为std::difference对于它们来说是 O(1) (它可以应用于所有双向迭代器,但这可能会慢一些 - 两个循环迭代 - 比not保留)。

我能想到的最有效的方法是保留必要的容量,然后插入元素(通过push_back or via insert)无需容量检查。

智能标准库实现可以调用reserve inside insert并且在插入时不检查容量。我不完全确定这会符合标准, 尽管。

如果你的图书馆足够聪明,安迪·徘徊的版本(见评论)就足够了:

dst.insert( dst.end(),
            std::make_move_iterator(src.begin()),
            std::make_move_iterator(src.end())    );

否则,您可以将调用写入reserve在调用之前手动insert,但是您不能(据我所知)插入/附加没有内部容量检查的元素:

template < typename T, typename FwdIt >
void append(FwdIt src_begin, FwdIt src_end, std::vector<T>& dst)
{
    dst.reserve( dst.size() + std::distance(src_begin, src_end) );
    // capacity checks might slow the loop inside `insert` down
    dst.insert(dst.end(), src_begin, src_end);
}

Example:

int main()
{
    std::vector<int> dst = { 0, 1, 2 };
    std::vector<int> src = { 3, 42 };

    append( std::make_move_iterator(src.begin()),
            std::make_move_iterator(src.end()),
            dst );
}

实施起来可能会更好append对于不同的迭代器类型:

template < typename T, typename FwdIt >
void append(FwdIt src_begin, FwdIt src_end, std::vector<T>& dst,
            std::forward_iterator_tag)
{
    // let the vector handle resizing
    dst.insert(dst.end(), src_begin, src_end);
}

template < typename T, typename RAIt >
void append(RAIt src_begin, RAIt src_end, std::vector<T>& dst,
            std::random_access_iterator_tag)
{
    dst.reserve( dst.size() + (src_end - src_begin) );
    dst.insert(dst.end(), src_begin, src_end);
}

template < typename T, typename FwdIt >
void append(FwdIt src_begin, FwdIt src_end, std::vector<T>& dst)
{
    append( src_begin, src_end, dst,
            typename std::iterator_traits<FwdIt>::iterator_category() );
}

如果由于循环内部的容量检查而出现性能问题,您可以首先尝试默认构造所需的附加元素。当它们存在时(即已构建),您可以使用非检查operator[]或简单的迭代器将 src 对象移动到目的地:

template < typename T, typename RAIt >
void append(RAIt src_begin, RAIt src_end, std::vector<T>& dst,
            std::random_access_iterator_tag)
{
    auto src_size = src_end - src_begin;

    dst.resize( dst.size() + src_size );

    // copy is not required to invoke capacity checks
    std::copy( src_begin, src_end, dst.end() - src_size );
    // ^this^ should move with the example provided above
}

方便包装:

template < typename T, typename FwdIt >
void append_move(FwdIt src_begin, FwdIt src_end, std::vector<T>& dst)
{
    append( std::make_move_iterator(src_begin),
            std::make_move_iterator(src_end),
            dst );
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

这是在 C++11 中将一个 std::vector 的内容移动到另一个 std::vector 的末尾的最有效方法吗? 的相关文章

  • SetWindowsHookEx 函数返回 NULL

    我正在研究 DLL 注入 但收到错误如下 挂接进程失败 87 参数不正确 目标进程和dll都是64位的 注入代码为 BOOL HookInjection TCHAR target TCHAR dll name https msdn micr
  • 没有 Unicode 字节顺序标记。无法切换到 Unicode

    我正在使用 XSD 编写 XML 验证器 下面是我所做的 但是当验证器到达该线时while list Read 它给了我错误 没有 Unicode 字节顺序标记 无法切换到 Unicode 有人可以帮我解决吗 public class Va
  • Android NDK C++“wstring”支持

    我有用 C 编写的源代码 lib 现在我想在 Android NDK 项目 NDK 6 中编译并使用相同的源代码 lib 我能够编译大多数 C 文件 除了基于 std wstring 的功能 在 Application mk 中 当我指定时
  • 使用不带参数的 Split() 时,默认分隔符是什么?

    所以我看了看String Split 今天 C 中的方法 我意识到你也可以向它传递零参数 这是我从未考虑过的 使用时默认的分隔符是什么Split 没有任何参数 如果没有值 则为空白 来源自here https msdn microsoft
  • 在 GCC 和 Clang 下,使用 lambda 的简单 RAII 包装器的复制初始化意外失败

    我在创建一个简单的 RAII 包装器时遇到了一个意想不到的问题 更不用说下面代码的逻辑不完整性了 复制构造函数和赋值运算符未删除等 这意味着是一个SSCCE 令我印象深刻的是复制初始化我的包装器与临时 lambda 的结果会导致编译错误 而
  • F10键没被抓住

    I have a Windows Form and there overriden ProcessCmdKey However this works with all of the F Keys except for F10 I am tr
  • 导出到 CSV 时 Gridview 出现空行

    这个问题是由进一步讨论引发的这个问题 https stackoverflow com questions 6674555 export gridview data into csv file 6674589 noredirect 1 com
  • 如何使用泛型类型的 DataContractSerializer 编写自定义序列化器?

    我想编写一个自定义序列化器 用于将会话状态存储到Azure 缓存 预览版 这意味着这个自定义序列化器必须实现IDataCacheObjectSerializer 如果我错了 请告诉我 我需要编写这个自定义序列化程序的原因是我需要序列化一些包
  • QThread - 使用槽 quit() 退出线程

    我想在线程完成运行时通知对象 但是 我无法让线程正确退出 我有以下代码 处理器 cpp thread new QThread tw new ThreadWorker connect tw SIGNAL updateStatus QStrin
  • 如何不在类中实现接口的功能?

    面试时面试官问了我以下问题 但我不知道这个问题的答案是什么 请帮忙 如果我不想 我必须做什么 在我的类中实现一个函数 在接口中声明为 由我班实施 Edited 我正在使用 NET 和 C 如果有人可以提供 C 示例代码示例 那就太好了 Th
  • 从给定的项目列表创建子列表

    我首先要说的是以下问题不是为了家庭作业目的即使因为我几个月前就完成了软件工程师的工作 无论如何 今天我正在工作 一位朋友向我询问了这个奇怪的排序问题 我有一个包含 1000 行的列表 每行代表一个数字 我想创建 10 个子列表 每个子列表都
  • 实时战略战争游戏人工智能算法

    我正在设计一款实时策略战争游戏 其中 AI 将负责控制大型六边形地图上的大量单位 可能超过 1000 个 一个单位有许多行动点 可以用于移动 攻击敌方单位或各种特殊行动 例如建造新单位 例如 一辆拥有 5 个行动点的坦克可以花费 3 个行动
  • `cosf`、`sinf` 等不在 `std` 中 [重复]

    这个问题在这里已经有答案了 根据这里的讨论 我有报告了一个错误 https bugs launchpad net ubuntu source gcc 8 bug 1831385给 Ubuntu 开发者 编译以下示例 C 程序时 includ
  • 有没有更好的方法来获取每个项目与谓词匹配的子序列?

    假设我有一个 IEnumerable 例如 2 1 42 0 9 6 5 3 8 我需要获得与谓词匹配的项目的 运行 例如 如果我的谓词是 bool isSmallerThanSix int number 我想得到以下输出 2 1 0 5
  • Dynamics Crm:获取状态代码/状态代码映射的元数据

    在 Dynamics CRM 2011 中 在事件实体上 状态原因 选项集 也称为状态代码 与 状态 选项集 也称为状态代码 相关 例如看这个截图 当我使用 API 检索状态原因选项集时 如下所示 RetrieveAttributeRequ
  • 将 bignum 类型结构转换为人类可读字符串的有效方法是什么?

    我有一点问题 为了增长我的 C 知识 我决定尝试实现一个基本的 bigint 库 bigint 结构的核心将是一个 32 位整数数组 选择它们是因为它们适合寄存器 这将允许我在数字之间进行操作 这些操作将在 64 位整数中溢出 这也将适合寄
  • 展开路径中具有环境变量的文件名

    最好的扩张方式是什么 MyPath filename txt to home user filename txt or MyPath filename txt to c Documents and settings user filenam
  • 在 SQL Server 上执行分页的最佳方式是什么?

    我有一个数据库超过200万记录 我需要执行分页以在我的 Web 应用程序上显示 该应用程序每页必须有 10 条记录DataGrid 我已经尝试使用ROW NUMBER 但是这种方式会选择所有 200 万条记录 然后只得到 10 条记录 我也
  • 正在获取“未终止 [] 设置”。 C# 中的错误

    我正在 C 中使用以下正则表达式 Regex find new Regex url
  • 如何使用 ASP.NET Web 表单从代码隐藏中访问更新面板内的文本框、标签

    我在更新面板中定义了一些控件 它们绑定到中继器控件 我需要根据匿名字段隐藏和显示用户名和国家 地区 但问题是我无法以编程方式访问更新面板中定义的控件 我如何访问这些控件 我也在网上查找但找不到很多参考资料 下面是来自aspx页面和 cs页面

随机推荐