push_back() 的奇怪行为 filesystem::path 的 .string().data() 结果变成了 "vector<const char *>"

Weird behavior of push_back() a filesystem::path's .string().data() result into a "vector<const char *>"

运行这个节目

#include <iostream>
#include <filesystem>
#include <vector>

using namespace std;
namespace fs = filesystem;

int main() {
    vector<fs::path> paths{"a.o", "b.o"};

    vector<const char *> argv{};
    for (auto &p : paths) {
        argv.push_back(p.string().data()); // line A
    }
    argv.push_back(paths[0].string().data());
    argv.push_back(paths[1].string().data());

    for (auto &s : argv) {
        cout << s << endl;
    }

    return 0;
}

得到

b.o
b.o
a.o
b.o

为什么 argv 的第一个元素不是 "a.o"?

我尝试在 A 行中断,发现当 "b.o" 是 push_back() 到 argv 时,argv 的第一个元素 更改为 从"a.o" 到 "b.o".

然后,当我将 A 行更改为

        argv.push_back(p.string().c_str()); // line A: .string().data() -> .string().c_str()

相同的结果。

当我将 A 行更改为

        argv.push_back(p.c_str()); // line A: .string().data() -> .c_str()

突然间我得到了预期的结果:

a.o
b.o
a.o
b.o

谁能解释奇怪的行为以及 .string().data() 和 .c_str() 之间的区别?

问题是 the path::string() function returns 字符串 by value.

一旦表达式 p.string().data() 结束,该值将被 破坏

这意味着指针将立即变为无效,当您尝试取消引用它时(例如打印时),您将得到 undefined behavior

显而易见的解决方案是不使用 char* 的向量作为字符串,而是使用 std::string 的向量。


至于使用p.string().data()(或p.string().c_str())和p.c_str()的区别,就是p.c_str() returns内部字符串的指针p 引用的 path 对象。该 path 对象将不会被破坏,直到您 clear 向量或向量(及其包含的对象)的生命周期结束(当向量被破坏时)。

请注意,如果您使用了

这样的循环
for (auto p : paths) { ... }

其中 ppath 对象的副本,那么即使使用 p.c_str() 也会遇到同样的问题,因为对象 p 将结束其life 并在循环的每次迭代结束时被破坏。