当前位置: 首页 > 知识库问答 >
问题:

STD::累计C++20版本

齐阎宝
2023-03-14

我试图理解这段代码,但我不明白为什么这个版本

for (; first != last; ++first) 
    init = std::move(init) + *first;

比这快

for (; first != last; ++first)
    init += *first;
template<class InputIt, class T>
T accumulate(InputIt first, InputIt last, T init)
{
    for (; first != last; ++first) {
        init = init + *first;
    }
    return init;
}

在C++20之后它变成了

template<class InputIt, class T>
constexpr // since C++20
T accumulate(InputIt first, InputIt last, T init)
{
    for (; first != last; ++first) {
        init = std::move(init) + *first; // std::move since C++20
    }
    return init;
}

我只是想知道,使用std::move是否有任何真正的改进。

编辑2

#include <utility>
#include <chrono>
#include <iostream>

using ck = std::chrono::high_resolution_clock;

std::string
test_no_move(std::string str) {

    std::string b = "t";
    int count = 0;

    while (++count < 100000)
        str = std::move(str) + b;   // Without std::move

    return str;
}

std::string
test_with_move(std::string str) {

    std::string b = "t";
    int count = 0;

    while (++count < 100000)        // With std::move
        str = str + b;

    return str;

}

int main()
{
    std::string result;
    auto start = ck::now();
    result = test_no_move("test");
    auto finish = ck::now();

    std::cout << "Test without std::move " << std::chrono::duration_cast<std::chrono::microseconds>(finish - start).count() << std::endl;

    start = ck::now();
    result = test_with_move("test");
    finish = ck::now();

    std::cout << "Test with std::move " << std::chrono::duration_cast<std::chrono::microseconds>(finish - start).count() << std::endl;

    return 0;
}

共有1个答案

莫欣悦
2023-03-14

对于具有非平凡move语义的类型,它可能更快。考虑累积std::vector 足够长的字符串:

std::vector<std::string> strings(100, std::string(100, ' '));

std::string init;
init.reserve(10000);
auto r = accumulate(strings.begin(), strings.end(), std::move(init));

对于不带std::move累加,

std::string operator+(const std::string&, const std::string&);

将被使用。在每次迭代中,它将为产生的字符串分配堆上的存储,以便在下一次迭代时丢弃它。

std::string operator+(std::string&&, const std::string&);
without std::move
n_allocs = 199

with std::move
n_allocs = 0

对于像int这样的内置类型,move只是一个拷贝--没有什么可以移动的。对于优化的构建,很可能会得到完全相同的程序集代码。如果您的基准测试显示任何速度提高/下降,很可能您没有正确地进行(没有优化、噪声、代码优化掉了,等等)。

 类似资料:
  • 我想知道为什么需要(又名reduce)第3个参数。对于那些不知道是什么的人,它的用法如下: 调用等同于: 还有可选第4个参数,它允许用任何其他操作替换加法。 我听说的一个基本原理是,如果你不需要加起来,而是乘一个向量的元素,我们需要其他的(非零)初始值: 但是为什么不像Python那样-为设置初始值,并使用从开始的范围。类似这样的事情: 这对任何行动都管用。为什么需要第三个参数?

  • 考虑以下示例代码,其中线程A在队列上推送函数,线程B在从队列中弹出时执行这些函数: 其中是一个并发队列,它有一个和一个函数,每个返回一个指示给定操作是否成功。因此,如果已满,则返回,如果为空,则返回。 现在我想知道代码是否在所有情况下都是线程安全的。让我们假设线程B的失败并即将调用

  • 自相矛盾的是,如果有一个像std::shared_ptr的指针但是不参与资源所有权共享的指针是很方便的。换句话说,是一个类似std::shared_ptr但不影响对象引用计数的指针。这种类型的智能指针必须要解决一个std::shared_ptr不存在的问题:可能指向已经销毁的对象。一个真正的智能指针应该跟踪所值对象,在悬空时知晓,悬空(dangle)就是指针指向的对象不再存在。这就是对std::w

  • 我正在尝试运行下面的C代码,我得到这个错误:任何人都可以帮我澄清为什么这是问题 输入:输入/text_4.txt 9 在抛出'std::bad_alloc'什么()的实例后终止调用:std::bad_alloc中止(核心转储)在读取了几个类似的线程后,解决方案是检查动态存储分配。但是,我的代码没有任何动态分配的内存 输入文件text_4.txt: 这是因为在波德莱尔三个年轻人的生活中,没有多少快乐

  • 问题内容: 我已经实现了一定数量的所有素数的列表。我试图做的事情很难解释,所以我只用一些硬代码展示一下: 所以从本质上讲,我试图从上一个列表中按顺序取出一个元素,然后按指数倍增,然后将其追加到其他列表中。 我意识到我可以做到这一点,这可能会更容易: 我需要一些想法才能在某种程度上做到这一点。 问题答案: 您需要 累积产品 清单。这是一个简单的食谱: 另一种方法,使用itertools: 或者,也许

  • 我想在Spark中做累积和。以下是注册表(输入): 配置单元查询: 输出: 使用火花逻辑,我得到相同的输出: 然而,当我在spark cluster上尝试这个逻辑时,的值将是累积和的一半,有时它是不同的。我不知道为什么它会发生在spark cluster上。是因为分区吗? 如何计算spark cluster上一列的累积和?