C++ c++;std::文件系统::路径追加上的std::bad_alloc
我正在经历一个非常奇怪的行为,我将其归结为一个非常基本的测试:C++ c++;std::文件系统::路径追加上的std::bad_alloc,c++,exception,gdb,c++17,libstdc++,C++,Exception,Gdb,C++17,Libstdc++,我正在经历一个非常奇怪的行为,我将其归结为一个非常基本的测试: #include <string> #include <filesystem> int main(void) { const std::string name = "foo"; const std::filesystem::path lock_dir = "/tmp"; std::filesystem::path lockfile = lock_dir / name; return 0;
#include <string>
#include <filesystem>
int main(void)
{
const std::string name = "foo";
const std::filesystem::path lock_dir = "/tmp";
std::filesystem::path lockfile = lock_dir / name;
return 0;
}
这带来了几个问题:
gcc版本8.3.0(Ubuntu 8.3.0-6ubuntu1~18.04.1)
,我的gdb是GNU gdb(Ubuntu 8.2-0ubuntu1~18.04)8.2
更新以下是成功编译的可执行文件的ldd输出
linux-vdso.so.1 (0x00007ffc697b2000)
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f5c35444000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f5c3522c000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f5c34e3b000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f5c34a9d000)
/lib64/ld-linux-x86-64.so.2 (0x00007f5c35a2d000)
我将用其他人在评论中的发现来总结我自己的发现。这还不是一个实际的答案,因为此时我无法解释失败的原因 通过在一个常规的
ubuntu
Docker映像中安装g++-8和g++-9,我能够重现这种行为,因此我有/usr/bin/g++-8
和/usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.26
根据gdb
堆栈跟踪,错误发生在std::vector
构造函数的某个地方。当在其操作符/
内调用std::filesystem::path
的默认复制构造函数时,似乎会发生这种情况:
/usr/include/c++/8/bits/fs\u path.h
///将一条路径附加到另一条路径
内联路径运算符/(常数路径和常数lhs,常数路径和常数rhs)
{
路径uu result(u lhs);//这是由Ubuntu的一个“功能”造成的,它提供了比系统附带的g++
更晚的libstdc++。因此
。有关更多详细信息,请参阅
通常,当使用GCC 8编译时,std::filesystem
符号不在libstdc++中。因此,如果您没有这样做,那么您将得到一个链接器错误。但是,由于GCC 9中较新的libstdc++.so
包含std::filesystem
的符号,因此链接器错误不会发生。不幸的是文件系统符号的CC 9版本与GCC 8头不兼容(因为文件系统库在GCC 8中是实验性的且不稳定的,并且对于GCC 9,filesystem::path
的布局已更改)。这意味着您的程序会链接,但在运行时,它使用了错误的符号来表示文件系统::path
,并且会发生不好的事情
我没有预料到这个问题,因为我不知道Ubuntu将旧的libstdc++头与新的libstdc++共享库混合在一起
我为Ubuntu建议的修复方法是使g++
自动将-lstdc++fs
添加到编译命令的末尾。如果您使用任何std::filesystem
功能,那么这些符号的正确定义应该在GCC 8的libstdc++fs.a
中找到(而不是在GCC 9的libstdc++.so
)在大多数情况下,一切都应该正常工作。如果Ubuntu还没有用这种解决方法更新他们的GCC包,你也可以通过确保手动链接到-lstdc++fs
(这是GCC 8所需的文档).如果在编译/链接时添加-lstdc++fs
您是否注意到任何差异?@tedlynmo如果没有它,他将有一个链接问题。我想知道在没有-lstdc++fs
的情况下如何使用gcc 8.3.0进行编译。它应该会失败…:-/这里是另一个数据点。在我的坏VM中,我可以用g++-std=c++17编译/链接一个坏的可执行文件-Wall-Wextra-Werror-g foo.cpp-o foo
。当我编译/链接g++-std=c++17-Wall-Wextra-Werror-g foo.cpp-o foo-lstdc++fs时,它运行时没有problem@TedLyngmo如果不是因为“功能”,你上面的评论是正确的,而且是100%正确的Ubuntu的。Ubuntu是根据设计为libstdc++提供不一致的头和共享库的,这就是为什么程序链接时不使用-lstdc++fs
,这就是为什么它在运行时崩溃的原因。有关更多信息,请参阅我下面的答案。它看起来像是构造函数调用了_M_spit_cmpts,这使得向量在某种程度上被阻塞了。“这个问题是由编译器和库版本之间的不兼容引起的”——你是对的,问题是OP没有造成这种不兼容,Ubuntu做到了:-(请参阅我的答案以了解谜题中缺失的部分。谢谢@JonathanWakely-你的答案应该是公认的:)哇…讨厌:-)很高兴知道!我正在运行Ubuntu。@TedLyngmo,是的,如果你忘记了-lstdc++fs
将变成未定义的行为和运行时崩溃,那么链接器应该是错误的。讨厌。根据,现在应该修复,所以请确保你是最新的。我的本地计算机可能会更新,但我可能应该检查一下它们在CI机器中的容器中运行的内容,boost::filesystem
也是混合的一部分。
linux-vdso.so.1 (0x00007ffc697b2000)
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f5c35444000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f5c3522c000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f5c34e3b000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f5c34a9d000)
/lib64/ld-linux-x86-64.so.2 (0x00007f5c35a2d000)