Google Sparsehash 在类型上使用 realloc(),这很难复制
Google Sparsehash uses realloc() on type which is not trivially copyable
考虑这个简单的程序:
#include <string>
#include <sparsehash/dense_hash_map>
int main()
{
google::dense_hash_map<std::string, int> map;
map["foo"] = 0;
}
使用 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)));
~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~
问题是:
- 是未定义的行为吗?
- 您能否建议可以应用于应用程序代码的修复或解决方法(而不是通过更改 Sparsehash 或避免使用它(?
- (奖励积分(你能构造一个因此而实际行为不端的程序吗(使用 std::string 或你自己的非平凡类型(? 到目前为止,我还没有看到使用 std::string 作为键类型的代码出现任何问题,尽管 std::string 一定是一种非常常用的键类型。
我在这里提出了一个问题:https://github.com/sparsehash/sparsehash/issues/149
-
是的,这是未定义的行为。
但是不要绝望,iffstd::string
不会在您的实现中存储任何内部指针,也不会在任何地方注册它们,无论如何它都会"工作";按位复制等效于在目标处进行移动构造并销毁源。
大多数(不是全部(字符串实现都是这种情况,无论是否 SSO。 -
如果可能使用不能保证具有简单破坏性可移动的类型,请使用不同的分配器(最后一个模板参数(来避免按位移动。
-
由于按位复制的无效移动而使程序爆炸是微不足道的。
将此类型与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(); } };
1. 是未定义的行为吗?是的。切勿使用 realloc(( 复制对象,因为有时它们具有指向资源的内部指针。当 2 个不同的对象运行其析构函数时,问题稍后会出现。现在,同一资源发生双重释放,完全不行。
2. 您能否建议可以应用于应用程序代码的修复或解决方法(而不是通过更改 Sparsehash 或避免使用它(?
尝试
#include <memory>
并更改行
google::dense_hash_map<std::string, int> map;
自
google::dense_hash_map<std::string, int, std::hash<std::string>, std::equal_to<std::string>, std::allocator> map;
现在,它不会使用谷歌的分配器libc_allocator_with_realloc
3. (奖励积分(你能构建一个因此而实际行为不端的程序吗(使用 std::string 或你自己的非平凡类型(?到目前为止,我还没有看到使用 std::string 作为键类型的代码出现任何问题,尽管 std::string 一定是一种非常常用的键类型。
不容易。因为你试图引起未定义的行为。在您的测试程序中,我会输入长度至少为 32 个字符的字符串,因此小字符串优化不会启动。并且可以在gcc的堆中进行一些测试,以查看它是否已损坏。看 1
我想这段代码预测了 c++20 类属性的简单可重定位。从本质上讲,这是一个可以安全地更改内存位置的对象。在 c++ 术语中,这是一个可以通过复制对象表示来安全地复制的对象,只要不再访问复制的对象,程序就会保持预期的行为,甚至不用于销毁。
例如,C++20 标准可能不会将此代码指定为"未定义的行为":
alignas(string) unsigned char buffer[sizeof(string)];
auto p = new(buffer) string{"test"};
alignas(string) unsigned char buffer2[sizeof(string)];
memcpy(buffer,buffer2,sizeof(string));//create a new string object copy of *p;
auto np = reinterpret_cast<string*>(buffer2);
(*np)[0]="r";
// the object at p shall not be accessed, not even destroyed.
如果类型具有引用其任何部分的非静态数据成员,则该类型不应轻松重定位:
struct fail{
int a;
int b;
int* selected;
fail(bool is_a,int i){
if (is_a){ a=i; selected=&a;}
else { b=i; selected=&b;}
}
};
链表容器的某些实现也不能简单地重新定位,例如,如果容器包含作为根节点的成员。因此,dense_hash_map
不能与那些自我记忆引用类型一起使用。
- Google Sparsehash 在类型上使用 realloc(),这很难复制
- 我很难理解这些关于检测整数溢出的评论
- 我很难修复我认为是双重免费的东西
- 这对我来说真的很难,我该怎么办
- 我很难弄清楚我哪里出了问题
- 我很难将冲刺动作绑定到虚幻钥匙中
- 我很难弄清楚为什么我的双重链接列表会崩溃,我可以找到一些方向
- "enable_if"使编译器很难生成可读的错误消息,这是怎么回事
- 很难从c++boost库中获得barycentric_riomic来按照我的需要工作
- 很难在我的C 程序中激活我的命令标志
- 使用嵌套环以简单(对我来说很难)输出
- 我很难理解这段代码
- 很难从文件中读取行并将其放入变量(C )
- 很难从我的二进制搜索树中删除一个节点
- 我很难理解指针
- 很难我的代码
- 我很难理解我在制作这个计算器时做错了什么
- 我很难将我的一些代码从 main 移动到函数
- 我很难从一年中的某天和午夜后的秒数中获取日期/时间组件
- 我很难理解编译错误/bin/sh:1:/a.out:在helloworld脚本中找不到