Please review my BST.

    //I don't know what to return when key is not found, so throw std::runtime_error()
    //this code does not have handwritten copy/move/destruct because I think unique_ptr already handled the node pointers
    template<typename T>
    using Ptr = std::unique_ptr<T>;
    
    template<typename K, typename V>
    class Bst {
    
    	struct Node {
    		K key;
    		V val;
    		std::size_t n;
    		Ptr<Node> left;
    		Ptr<Node> right;
    
    		Node(const K& k, const V& v, const std::size_t& s) : key(k), val(v), n(s), left(nullptr), right(nullptr) { }
    	};
    
    	Ptr<Node> root;
    
    public:
    
    	Bst() :root{nullptr} { }
    
    	std::size_t size() {
    		return size(root);
    	}
    
    	std::size_t size(Ptr<Node>& t) {
    		return (t == nullptr) ? 0 : t->n;
    	}
    
    	void insert(const K& k, const V& v) {
    		root = insert(root,k,v);
    	}
    
    	Ptr<Node> insert(Ptr<Node>& t, const K& k, const V& v) {
    		if (t == nullptr) {
    			Ptr<Node> node(new Node(k,v,1));
    			return node;
    		}
    		if (k < t->key) {
    			t->left = insert(t->left,k,v);
    		} else if (k > t->key) {
    			t->right = insert(t->right,k,v);
    		} else {
    			t->val = v;
    		}
    		t->n = size(t->left)+size(t->right)+1;
    		return std::move(t);
    	}
    
    	V& operator[](const K& k) {
    		return get(root,k);
    	}
    
    	V& get(Ptr<Node>& t, const K& k) {
    		if (t == nullptr) {
    			t = insert(t,k,{});
    			return t->val;
    		}
    		if (k < t->key) {
    			return get(t->left,k);
    		} else if (k > t->key) {
    			return get(t->right,k);
    		} else {
    			return t->val;
    		}
    	}
    
    	K min() {
    		check(root);
    		return min(root)->key;;
    	}
    
    	Ptr<Node>& min(Ptr<Node>& t) {
    		if (t->left == nullptr) {
    			return t;
    		}
    		return min(t->left);
    	}
    
    	K max() {
    		check(root);
    		return max(root)->key;
    	}
    
    	Ptr<Node>& max(Ptr<Node>& t) {
    		if (t->right == nullptr) {
    			return t;
    		}
    		return max(t->right);
    	}
    
    	void remove_min() {
    		check(root);
    		root = remove_min(root);
    	}
    
    	Ptr<Node> remove_min(Ptr<Node>& t) {
    		if (t->left == nullptr) {
    			return std::move(t->right);
    		}
    		t->left = remove_min(t->left);
    		t->n = size(t->left)+size(t->right)+1;
    		return std::move(t);
    	}
    
    	void remove_max() {
    		check(root);
    		root = remove_max(root);
    	}
    
    	Ptr<Node> remove_max(Ptr<Node>& t) {
    		if (t->right == nullptr) {
    			return std::move(t->left);
    		}
    		t->right = remove_max(t->right);
    		t->n = size(t->left)+size(t->right)+1;
    		return std::move(t);
    	}
    
    	void remove(K k) {
    		root = remove(root, k);
    	}
    
    	Ptr<Node> remove(Ptr<Node>& t, K k) {
    		check(t);
    		if      (k < t->key){
    			t->left = remove(t->left,  k);
    		} else if (k > t->key){
    			t->right = remove(t->right, k);
    		} else {
    			if (t->right == nullptr) {
    				return std::move(t->left);
    			}
    			if (t->left == nullptr) {
    				return std::move(t->right);
    			}
    			Ptr<Node> d = std::move(t);
    			t = std::move(min(d->right));
    			t->right = remove_min(t);
    			t->left = std::move(d->left);
    		}
    		t->n = size(t->left)+size(t->right)+1;
    		return std::move(t);
    	}
    
    	void traverse() {
    		traverse(root);
    	}
    
    	void traverse(Ptr<Node>& t) {
    		if (t == nullptr) {
    			return;
    		}
    		traverse(t->left);
    		std::cout << t->key << " " << t->val << '\n';
    		traverse(t->right);
    	}
    
    private:
    
    	void check(Ptr<Node>& t) {
    		if (t == nullptr) {
    			throw std::runtime_error("No node");
    		}
    	}
    
    };

How is my usage of std::unique_ptr() and std::move()? How can I improve this code?