Почему std :: string_view создает висячее представление в троичном выражении? - PullRequest
19 голосов
/ 17 июня 2019

Рассмотрим метод, который возвращает 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 Ответ

24 голосов
/ 17 июня 2019

Потому что так работает условный оператор.

Вы вызываете ?: для двух операндов, один из которых является lvalue типа std::string const, а другой - lvalue типа char const[1]. Правило языка для условного оператора ... действительно сложное. соответствующее правило :

В противном случае, , если второй и третий операнд имеют разные типы и имеют либо (возможно, cv-квалифицированный) тип класса , либо оба являются glvalues ​​одной и той же категории значений и того же типа, за исключением cv -квалификация, сделана попытка сформировать неявную последовательность преобразования из каждого из этих операндов в тип другого. [ Примечание : свойства, такие как доступ, является ли операнд битовым полем, или функция преобразования удалена, для этого определения игнорируются. - примечание конца]. Делаются попытки сформировать неявную последовательность преобразования из выражения операнда E1 типа T1 в целевой тип, связанный с типом T2 выражения операнда E2, следующим образом:

  • Если E2 является lvalue, целевым типом является «lvalue ссылка на T2», при условии ограничения, что при преобразовании ссылка должна напрямую связываться ([dcl.init.ref]) с glvalue.
  • Если E2 - xvalue, [...]
  • Если E2 является значением или если ни одна из приведенных выше последовательностей преобразования не может быть сформирована, и хотя бы один из операндов имеет (возможно, cv-квалифицированный) тип класса :

    • , если T1 и T2 относятся к одному и тому же типу класса [...]
    • в противном случае, если T2 является базовым классом T1, [...]
    • в противном случае тип назначения - это тип, который E2 будет иметь после применения стандартных преобразований lvalue-to-rvalue, array-to-pointer и function-to-pointer.

Используя этот процесс, определяется, может ли последовательность неявного преобразования быть сформирована из второго операнда в целевой тип, определенный для третьего операнда, и наоборот. Если обе последовательности могут быть сформированы, или одна может быть сформирована, но это неоднозначная последовательность преобразования, программа является некорректной. Если последовательность преобразования не может быть сформирована, операнды остаются без изменений, и дальнейшая проверка выполняется, как описано ниже. В противном случае, если может быть сформирована ровно одна последовательность преобразования, это преобразование применяется к выбранному операнду, а преобразованный операнд используется вместо исходного операнда для оставшейся части этого подпункта. [ Примечание : преобразование может быть некорректным, даже если может быть сформирована неявная последовательность преобразования. - конечная нота ]

Невозможно преобразовать std::string const в char const(&)[1] или char const*, но вы можете преобразовать char const[1] в std::string const (внутренняя вложенная пуля) ... так что вы получить. Значение типа 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;
}

Результатом этого условного оператора является string_view, причем оба преобразования безопасны.

...