Возвращаясь к расширению времени жизни в C ++, я обнаружил, что есть некоторые шаблоны, которые нарушают «разложимость» выражений C ++. Например, следующие два блока представляют собой допустимый код C ++ :
class NonMovable {
public:
NonMovable(NonMovable&&) = delete;
NonMovable(const NonMovable&) = delete;
NonMovable();
int Value() const;
};
template <class T>
const T& identity(const T& x) {
return x;
}
template <class T>
class A {
public:
explicit A(const T& value) : value_(value) {}
const T& GetValue() const {
return value_;
}
private:
const T& value_;
};
Правильное использование:
int main() {
int retcode = identity(
identity(/*tmp1*/ A(/*tmp2*/ NonMovable{}).GetValue())).Value();
// tmp1 and tmp2 end their lifetimes here:
// their full-expression is the whole previous line
return retcode;
}
Но если мы разложим первое выражение в main
, он становится недействительным:
int main() {
auto&& a_obj = /*tmp1*/ A(/*tmp2*/ NonMovable{});
// tmp2 lifetime ends here
// oops! dereferencing dangling reference:
int retcode = identity(
identity(a_obj.GetValue())).Value();
return retcode;
// tmp1 lifetime ends here
}
Мой вопрос: Возможно ли отключить второй вид использования?
PS: я не совсем уверенесли второй main
вводит UB, потому что я протестировал с clang -Wlifetime
, и он не жалуется. Но я все еще верю, что это UB. В реальной жизни я сталкивался с подобным поведением: код ломался, выдавая предупреждения UBSan и ошибки по умолчанию, если я разложил одно выражение на два отдельных.
PPS: эти identity
s на самом деле не имеют значениямного, если я правильно понимаю время жизни объекта (в чем я сейчас сомневаюсь)