为什么std :: string_view在三元表达式中创建悬空视图?

时间:2019-06-17 15:15:02

标签: c++ language-lawyer c++17 dangling-pointer string-view

请考虑从返回std::string_view的方法或从空字符串返回const std::string&的方法。令我惊讶的是,以这种方式编写方法会导致字符串视图悬空:

const std::string& otherMethod();

std::string_view myMethod(bool bla) {
    return bla ? otherMethod() : ""; // Dangling view!
}

https://godbolt.org/z/1Hu_p2

似乎编译器首先将std::string的结果的临时otherMethod()放在堆栈上,然后返回此临时副本的视图,而不仅仅是返回引用的视图。首先,我想到了一个comipler错误,但是G ++和clang都这样做。

修复很容易:将otherMethod包装为string_view的显式构造即可解决问题:

std::string_view myMethod(bool bla) {
    return bla ? std::string_view(otherMethod()) : ""; // Works as intended!
}

https://godbolt.org/z/Q-sEkr

为什么会这样?为什么原始代码会在没有警告的情况下创建隐式副本?

1 个答案:

答案 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限定的)类类型

         
        
    • 如果T2T1是相同的类类型[...]
    •   
    • 否则,如果T2T2的基类,则[...]
    •   
    • 否则,目标类型是E2在应用从左值到右值,数组到指针和函数到指针的标准转换后的类型。
    •   
  •   
     

使用此过程,确定是否可以形成从第二操作数到为第三操作数确定的目标类型的隐式转换序列,反之亦然。如果可以形成两个序列,或者可以形成一个序列,但是这是模棱两可的转换序列,则程序格式不正确。如果无法形成转换序列,则操作数保持不变,并按如下所述进行进一步检查。否则,如果恰好可以形成一个转换序列,则将该转换应用于所选的操作数,并在此子节的其余部分中使用转换后的操作数代替原始操作数。 [注意:即使可以形成隐式转换序列,转换也可能格式错误。 — 尾注]

无法将T1转换为std::string constchar 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; } ,两次转换都是安全的。