uninitialized_copy memcpy / memmove优化

问题描述 投票:3回答:1

我最近开始研究MSVC实现中的STL。那里有一些不错的技巧,但是我不知道为什么使用以下标准。

如果满足某些条件,std::uninitialized_copy将优化为简单的memcpy/memmove。据我了解,如果源类型T的目标类型U memcpy,则输入范围可以is_trivially_copy_constructible到未初始化的区域。

但是,MSVC实现在选择memcpy之前要检查很多事情,而不是元素的一对一复制构造。我不想在此处粘贴相关代码,如果有人感兴趣,我将通过pastebin共享它:https://pastebin.com/Sa4Q7Qj0

uninitialized_copy的基本算法是这样的(为便于阅读,省略了异常处理)

template <typename T, typename... Args>
inline void construct_in_place(T& obj, Args&&... args)
{
    ::new (static_cast<void*>(addressof(obj)) T(forward<Args>(args)...);
}

template <typename In, typename Out>
inline Out uninitialized_copy(In first, In last, Out dest)
{
    for (; first != last; ++first, ++dest)
        construct_in_place(*dest, *first);
}

如果复制构造不执行任何“特殊”操作(通常是可复制构造的,则可以将其优化为memcpy/memmove

MS的实现需要以下条件:

  • T可微分配给U
  • T可轻松复制到U
  • 不重要
  • 如果T!= U,则进行额外检查(如sizeof(T)== sizeof(U))>
  • 例如,以下结构不能memcpy

struct Foo
{
    int i;
    Foo() : i(10) { }
};

但以下内容可以:

struct Foo
{
    int i;
    Foo() = default; // or simply omit
};

仅检查类型U是否可以从类型T中复制构造就足够了吗?因为所有这些都是uninitialized_copy所做的。

例如,我看不到为什么MS的STL实现不对以下内容进行注释(注意:我知道原因,它是用户定义的构造函数,但我不理解其背后的逻辑) :

struct Foo
{
    int i;

    Foo() noexcept
        : i(10)
    {
    }

    Foo(const Foo&) = default;
};

void test()
{
    // please forgive me...
    uint8 raw[256];
    Foo* dest = (Foo*)raw;
    Foo src[] = { Foo(), Foo() };

    bool b = std::is_trivially_copy_constructible<Foo>::value;  // true
    bool b2 = std::is_trivially_copyable<Foo>::value;           // true

    memcpy(dest, src, sizeof(src)); // seems ok

    // uninitialized_copy does not use memcpy/memmove, it calls the copy-ctor one-by-one
    std::uninitialized_copy(src, src + sizeof(src) / sizeof(src[0]), dest);
}

相关SO帖子:Why doesn't gcc use memmove in std::uninitialized_copy?

更新

正如@Igor Tandetnik在评论中指出的那样,如果没有用户定义的副本构造函数,则类型T几乎是可复制构造的,这是不安全的。他提供了以下示例:

struct Foo
{
    std::string data;
};

在此示例中,没有用户定义的副本构造函数,并且仍然不能轻易实现副本构造。谢谢您的纠正,我根据反馈修改了原始帖子。

我最近开始研究MSVC实现中的STL。那里有一些不错的技巧,但是我不知道为什么使用以下标准。 std :: uninitialized_copy是...

c++ visual-c++ stl
1个回答
3
投票

uninitialized_copy有两个职责:首先,必须确保正确的位模式进入目标缓冲区。其次,它必须根据我非常不完整的研究,看来现在只有trivially copyable类型可以保证由memcpy / memmove保留其位模式;记忆任何其他类型的类型(即使它恰好是普通可复制构造的和/或普通可复制赋值的!)也会产生不确定的行为。

© www.soinside.com 2019 - 2024. All rights reserved.