Google Sparsehash在类型上使用realloc(),这不是简单的可复制

Joh*_*nck 6 c++ realloc undefined-behavior sparsehash gcc8

考虑这个简单的程序:

#include <string>
#include <sparsehash/dense_hash_map>

int main()
{
    google::dense_hash_map<std::string, int> map;
    map["foo"] = 0;
}
Run Code Online (Sandbox Code Playgroud)

使用GCC 8.2和-Wclass-memaccess(或-Wall)进行编译会产生警告:

sparsehash/internal/libc_allocator_with_realloc.h:68:40: warning:
‘void* realloc(void*, size_t)’ moving an object of non-trivially copyable type
    ‘struct std::pair<const std::__cxx11::basic_string<char>, int>’;
use ‘new’ and ‘delete’ instead [-Wclass-memaccess]
    return static_cast<pointer>(realloc(p, n * sizeof(value_type)));
                                ~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~
Run Code Online (Sandbox Code Playgroud)

问题是:

  1. 是不确定的行为?
  2. 您能否建议可以应用于应用程序代码的修复或解决方法(不是通过更改Sparsehash或避免使用它)?
  3. (加分点)你能构造一个实际上由于这个而行为不正常的程序(使用std :: string或你自己的非平凡类型)?到目前为止,我还没有看到使用std :: string作为密钥类型的代码中的任何问题,尽管std :: string必须是一个非常常用的密钥类型.

我在这里提出了一个问题:https://github.com/sparsehash/sparsehash/issues/149

Ded*_*tor 2

  1. 是的,这是未定义的行为。
    但不要绝望, iffstd::string不会在您的实现中存储任何内部指针,也不在任何地方注册它们,无论如何它都会“工作”;进行按位复制相当于在目标处进行移动构造并破坏源。大多数(不是全部)字符串实现
    都是这种情况,无论是否是 SSO。

  2. 如果您可能使用不保证可破坏性移动的类型,请使用不同的分配器(最后一个模板参数)来避免按位移动。

  3. 由于按位复制的无效移动而导致程序崩溃是微不足道的。
    使用此类型google::dense_hash_map

    class bang {
        bang* p;
    public:
        bang() : p(this) {}
        bang(bang const&) : bang() {}
        bang& operator=(bang const&) { return *this; }
        ~bang() { if (p != this) std::abort(); }
    };
    
    Run Code Online (Sandbox Code Playgroud)