c++大括号初始化列表,临时生命周期

C++ brace initializer list, temporary lifetime

本文关键字:生命 周期 列表 初始化 c++      更新时间:2023-10-16

我有以下代码:

string join(initializer_list<string_view> strings);

initializer_list是std::initializer_list, string_view不是std::string视图,但是非常类似的类,构造函数来自const string&和const char*。

然后我有以下join的调用:

EXPECT_EQ("this", join({ string("this") }));

经过调查,我发现结果初始化列表的第一个元素不是"this",而是"his"。这是因为string("this")创建的临时变量的析构函数在string_view创建之后被调用(因此它包含无效指针)。为什么string("this")的生命周期没有扩展到完整表达式EXPECT_EQ("this", join({ string("this") }));的末尾?

<标题>编辑

好的,正如你所建议的,有一个独立的例子:

#include <iostream>
#include <string>
using namespace std;
class string_view {
public:
    string_view(const string& str)
        : _begin(str.data())
        , _end(str.data() + str.size()) {
    std::cout << "string_view(...)" << std::endl;
    }
    const char* _begin;
    const char* _end;
};
void join(initializer_list<string_view> strings) {
    std::cout << "join(...)" << std::endl;
    for (auto i = 0u; i < 5; ++i) {
        std::cout << int(strings.begin()->_begin[i]) << " " << strings.begin()->_begin[i] << std::endl;
    }
}
int main() {
    join({ string("this") });
    return 0;
}

用最新的Visual Studio c++ (Express)编译的程序输出:

string_view(...)
join(...)
0
104 h
105 i
115 s
0

它可能因编译器的不同而不同,因为上面的程序可能是病态的。

我已经研究了调试器中调用的顺序,有一个顺序:

main()
    basic_string(const char*)
    string_view(const string&)
    ~basic_string()
    initializer_list(...)
    join(...)

我希望string("this")的内容可以在join函数中使用。但事实并非如此,因为' string("this")之前就被销毁了。

为什么在调用join函数之前调用临时字符串string("this")的析构函数,或者换句话说,为什么不将string("this")的生存期扩展到完整表达式join({ string("this") })的末尾?

我认为可能在这里发生的是,你正在构造你的initializer_list从一个引用到你的字符串,所以当你的initializer_list已经完成构造字符串超出作用域。

您看到您的字符串不是join()函数的参数,而是您构造的initializer_list的参数。

我的猜测是initializer_list是构造的,您的字符串超出范围,然后您的join()函数试图通过包含在string_view中的引用访问死字符串。