为什么复制和移动构造函数以相同数量的 memcopies 结束?

Why the copy and move constructors end up in the same amount of memcopies?

这里的问题是了解在由函数的 return 对象初始化向量时是否调用了复制或移动构造函数。 使用探查器检查 mallocs 在两种情况下显示相似的 memcopies。为什么?

我们有 "message" 类型的 class。 class 提供了一个函数 "data_copy",它 return 将 "message" 的内容作为向量。

我尝试了 2 个选项。 一种是直接使用拷贝构造函数初始化一个新的vector

std::vector<uint8_t> vector1 ( message.data_copy() );

第二个选项是尽量避免额外的副本并做

std::vector<uint8_t> vector1 ( std::move( message.data_copy() ) );

作为参考,我附上 data_copy() 的作用。

std::vector<uint8_t> message::data_copy(void) const
{
    std::vector<uint8_t> d(this->size());
    copy_data_to_buffer(d.data());
    return d;
}
void message::copy_data_to_buffer(uint8_t* buffer) const
{
    DEBUG_LOG("copy_data_to_buffer");
    for(const fragment* p = &head; p != nullptr; p = p->next)
    {
        memcpy(buffer, p->data[0], p->size[0]);
        buffer += p->size[0];

        if(p->size[1])
        {
            memcpy(buffer, p->data[1], p->size[1]);
            buffer += p->size[1];
        }
    }
}

最后,通过使用分析器,我比较了 malloc 调用的数量。虽然人们会期望移动构造函数在现实中避免额外的 memcopy,但它们在两种情况下都是相同的。

您两次都在使用移动构造函数。 data_copy 的结果是临时的。然后你用这个临时参数构造 vector 。于是出招了。

第二次,您基本上是将已经是右值引用的对象强制转换为右值引用,因此再次使用了移动构造函数。两者的行为应该完全没有区别。

我想,您可能误解了什么是临时文件,什么是右值引用。您不必经常使用 ::std::move,如果您正在使用它,您应该仔细查看您正在使用它的代码,以确保您做的是正确的事情。

Godbolt 上的此代码是 proof that the copy constructor is never called。这是链接到的代码:

#include <utility>

struct ICantBeCopied {
    ICantBeCopied(ICantBeCopied const &) = delete;
    ICantBeCopied(ICantBeCopied &&) {}
    ICantBeCopied() {}
};

ICantBeCopied make_a_copy()
{
    ICantBeCopied c;
    return c;
}

void a_function()
{
    ICantBeCopied a{make_a_copy()};
    ICantBeCopied b{::std::move(make_a_copy())};
}