为什么std :: string_view在三元表达式中创建悬空视图? [英] Why does std::string_view create a dangling view in a ternary expression?

查看:126
本文介绍了为什么std :: string_view在三元表达式中创建悬空视图?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Consider a method that returns a std::string_view either from a method that returns a const std::string& or from an empty string. To my surprise, writing the method this way results in a dangling string view:

const std::string& otherMethod();

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

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

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

It seems that the compiler first puts a temporary std::string copy of the result of otherMethod() on the stack and then returns a view of this temporary copy instead of just returning a view of the reference. First I thought about a comipler bug, but both G++ and clang do this.

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

The fix is easy: Wrapping otherMethod into an explicit construction of string_view solves the issue:

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

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

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

Why is this the case? Why does the original code create an implicit copy without warning?

推荐答案

因为这就是条件运算符的工作方式.

Because that's how the conditional operator works.

您要在两个操作数上调用?:,其中一个是类型为std::string const的左值,另一个是类型为char const[1]的左值.条件运算符的语言规则非常复杂. 相关规则为:

You're invoking ?: on two operands, one of which is an lvalue of type std::string const and the other is an lvalue of type char const[1]. The language rule for the conditional operator is... really complicated. The relevant rule is:

否则,如果第二个操作数和第三个操作数具有不同的类型,并且具有(可能具有cv限定的)类类型,或者如果两个都是相同值类别和相同类型的glvalues,除了 cv 限定,尝试形成从这些操作数中的每个操作数到另一个操作数的类型的隐式转换序列. [注意:对于该确定,将忽略诸如访问,操作数是否为位字段或是否删除转换函数之类的属性. [end note]尝试从类型T1的操作数表达式E1到与该操作数表达式E2的类型T2相关的目标类型形成隐式转换序列,如下所示:

Otherwise, if the second and third operand have different types and either has (possibly cv-qualified) class type, or if both are glvalues of the same value category and the same type except for cv-qualification, an attempt is made to form an implicit conversion sequence from each of those operands to the type of the other. [ Note: Properties such as access, whether an operand is a bit-field, or whether a conversion function is deleted are ignored for that determination. — end note ] Attempts are made to form an implicit conversion sequence from an operand expression E1 of type T1 to a target type related to the type T2 of the operand expression E2 as follows:

  • 如果E2是左值,则目标类型为对T2的左值引用",但要受约束,即转换中引用必须直接绑定([dcl.init.ref])到glvalue. li>
  • 如果E2是一个xvalue,则[...]
  • 如果E2是prvalue ,或者以上转换序列都不能形成,并且至少一个操作数具有(可能是cv限定的)类类型:

  • If E2 is an lvalue, the target type is "lvalue reference to T2", subject to the constraint that in the conversion the reference must bind directly ([dcl.init.ref]) to a glvalue.
  • If E2 is an xvalue, [...]
  • If E2 is a prvalue or if neither of the conversion sequences above can be formed and at least one of the operands has (possibly cv-qualified) class type:

  • 如果T1T2是相同的类类型[...]
  • 否则,如果T2T1的基类,则[...]
  • 否则,目标类型是E2在应用从左值到右值,数组到指针和函数到指针的标准转换后的类型.
  • if T1 and T2 are the same class type [...]
  • otherwise, if T2 is a base class of T1, [...]
  • otherwise, the target type is the type that E2 would have after applying the lvalue-to-rvalue, array-to-pointer, and function-to-pointer standard conversions.

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

Using this process, it is determined whether an implicit conversion sequence can be formed from the second operand to the target type determined for the third operand, and vice versa. If both sequences can be formed, or one can be formed but it is the ambiguous conversion sequence, the program is ill-formed. If no conversion sequence can be formed, the operands are left unchanged and further checking is performed as described below. Otherwise, if exactly one conversion sequence can be formed, that conversion is applied to the chosen operand and the converted operand is used in place of the original operand for the remainder of this subclause. [ Note: The conversion might be ill-formed even if an implicit conversion sequence could be formed. — end note ]

无法将std::string const转换为char const(&)[1]char const*,但是您可以将 char const[1]转换为std::string const(内部嵌套的项目符号)...你得到.类型为std::string const的prvalue.也就是说,您要么复制一个字符串,要么构造一个新的字符串...无论哪种方式,您都将string_view返回到立即超出范围的临时目录.

Can't convert std::string const to either char const(&)[1] or char const*, but you can convert char const[1] to std::string const (the inner nested bullet)... so that's what you get. A prvalue of type std::string const. Which is to say, you're either copying one string or constructing a new one... either way, you're returning a string_view to a temporary which goes out of scope immediately.

您想要的就是拥有的东西

What you want is either what you had:

std::string_view myMethod(bool bla) {
    return bla ? std::string_view(otherMethod()) : "";
}

或:

std::string_view myMethod(bool bla) {
    return bla ? otherMethod() : ""sv;
}

该条件运算符的结果为string_view,两次转换均是安全的.

The result of that conditional operator is a string_view, with both conversions being safe.

这篇关于为什么std :: string_view在三元表达式中创建悬空视图?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆