Почему после вызова t1 = std::move(t0) вызывается деструктор t0?
Насколько я понял идею std::move, вызов в return move(t) должен осуществить перемещение данных класса t в t1 без вызова деструктора.
Насколько я понимаю идею std::move, вы получаете вызов конструктора T(T&&) вместо T(const T&), в котором вы сами должны переместить (или не перемещать) нужные поля в создаваемый объект. После перемещения оригиналы полей нужно подходящим способом "обнулить". Деструктор локального объекта, выходящего из области видимости будет вызван в любом случае.В реальной программе T содержит объект fstream, а в деструкторе осуществляется закрытие потока.
Насколько я вижу, std::fstream поддерживает конструирование из ссылки на rvalue.
А почему не должен? Конструируется новый объект, а старый при этом будет удалён, но move конструктор как бы намекает, что не нужно использовать глубокое копирование в конструкторе. Нужно просто скопировать указатели и всё, при этом да, в исходном объекте указатели нужно обнулить. А в вашем случае от move конструктора толку вообще нет и не будет. Ибо нет никакого смысла в move конструкторе для примитивных типов, которые при этом создаются на стеке.