请考虑从返回std::string_view
的方法或从空字符串返回const std::string&
的方法。令我惊讶的是,以这种方式编写方法会导致字符串视图悬空:
const std::string& otherMethod();
std::string_view myMethod(bool bla) {
return bla ? otherMethod() : ""; // Dangling view!
}
似乎编译器首先将std::string
的结果的临时otherMethod()
放在堆栈上,然后返回此临时副本的视图,而不仅仅是返回引用的视图。首先,我想到了一个comipler错误,但是G ++和clang都这样做。
修复很容易:将otherMethod
包装为string_view
的显式构造即可解决问题:
std::string_view myMethod(bool bla) {
return bla ? std::string_view(otherMethod()) : ""; // Works as intended!
}
为什么会这样?为什么原始代码会在没有警告的情况下创建隐式副本?
答案 0 :(得分:24)
因为这就是条件运算符的工作方式。
您要在两个操作数上调用?:
,其中一个是类型std::string const
的左值,另一个是类型char const[1]
的左值。条件运算符的语言规则非常复杂。 relevant rule是:
否则,如果第二个操作数和第三个操作数具有不同的类型,并且具有(可能具有cv限定的)类类型,或者如果两个都是相同值类别和相同类型的glvalues,除了< em> cv 限定,尝试形成从这些操作数中的每个到另一个类型的隐式转换序列。 [注意:对于该确定,将忽略诸如访问,操作数是否为位字段或是否删除转换函数之类的属性。 [end note]尝试形成从类型
E1
的操作数表达式T1
到与操作数表达式{{1}的类型T2
相关的目标类型的隐式转换序列},如下所示:
- 如果E2是左值,则目标类型为“对
E2
的左值引用”,但要受约束,即在转换中引用必须直接绑定([dcl.init.ref])到glvalue。- 如果E2是一个xvalue,则[...]
如果E2是prvalue ,或者以上转换序列都不能形成,并且至少一个操作数具有(可能是cv限定的)类类型:
- 如果
T2
和T1
是相同的类类型[...]- 否则,如果
T2
是T2
的基类,则[...]- 否则,目标类型是E2在应用从左值到右值,数组到指针和函数到指针的标准转换后的类型。
使用此过程,确定是否可以形成从第二操作数到为第三操作数确定的目标类型的隐式转换序列,反之亦然。如果可以形成两个序列,或者可以形成一个序列,但是这是模棱两可的转换序列,则程序格式不正确。如果无法形成转换序列,则操作数保持不变,并按如下所述进行进一步检查。否则,如果恰好可以形成一个转换序列,则将该转换应用于所选的操作数,并在此子节的其余部分中使用转换后的操作数代替原始操作数。 [注意:即使可以形成隐式转换序列,转换也可能格式错误。 — 尾注]
无法将T1
转换为std::string const
或char const(&)[1]
,但是您可以将 char const*
转换为char const[1]
(内部嵌套的子弹)...这就是您得到的。类型为std::string const
的prvalue。也就是说,您要么复制一个字符串,要么构造一个新的字符串……无论哪种方式,您都将std::string const
返回到立即超出范围的临时目录。
您想要的就是拥有的东西
string_view
或:
std::string_view myMethod(bool bla) {
return bla ? std::string_view(otherMethod()) : "";
}
该条件运算符的结果为std::string_view myMethod(bool bla) {
return bla ? otherMethod() : ""sv;
}
,两次转换都是安全的。