If we return a pair whose .first is the head and .second is the tail, it will be easier to append T to the left and right sublists than the original solution, and simpler to read too.
pair< shared_ptr<BSTNode<int>>, shared_ptr < BSTNode<int> > > BSTToDoublyList(
const shared_ptr<BSTNode<int>>& T) {
if (!T) {
return{ nullptr, nullptr };
}
auto l = BSTToDoublyList(T->left);
auto r = BSTToDoublyList(T->right);
//attach T to left's tail
T->left = l.second;
if (l.second)
{
l.second->right = T;
}
//attach right's head to T
T->right = r.first;
if (r.first)
{
r.first->left = T;
}
//head and tail should be valid nodes
return{ l.first ? l.first : T, r.second ? r.second : T };
}