Binary Search Tree implementation with unique pointers I have implemented a binary search tree using templates and unique_ptr in C++ 11. At present, only insertion and deletion are implemented. Please provide your feedback for improvements.

#include &lt;iostream&gt;
#include &lt;memory&gt;


template&lt;typename T&gt;
class Btree {
public:
    void insert(T data)
    {
        _insert(root, data);
    }
    void traverse(void (*func)(T data))
    {
        _traverse(root, func);
    }
    void del(T data)
    {
        _del(root, data);
    }
private:
    struct node {
        T data;
        std::unique_ptr&lt;node&gt; left, right;
        node(T data): data(data), left(nullptr), right(nullptr) {}
    };

    std::unique_ptr&lt;node&gt; root;
    void _insert(std::unique_ptr&lt;node&gt;&amp; curr, T data);
    void _del(std::unique_ptr&lt;node&gt;&amp; curr, T data);
    void _traverse(std::unique_ptr&lt;node&gt;&amp; curr, void (*func)(T data));
    T _findmin(std::unique_ptr&lt;node&gt; &amp;curr);
};

template&lt;typename T&gt;
void Btree&lt;T&gt;::_insert(std::unique_ptr&lt;node&gt;&amp; curr, T data)
{
    if (curr == nullptr) {
        curr.reset(new node(data));
        return;
    }

    if (data &lt; curr-&gt;data)
        _insert(curr-&gt;left, data);
    else
        _insert(curr-&gt;right, data);
}
template&lt;typename T&gt;
T Btree&lt;T&gt;::_findmin(std::unique_ptr&lt;node&gt;&amp; curr)
{
    if (curr &amp;&amp; curr-&gt;left == nullptr)
        return curr-&gt;data;
    return _findmin(curr-&gt;left);    
}
template&lt;typename T&gt;
void Btree&lt;T&gt;::_del(std::unique_ptr&lt;node&gt;&amp; curr, T data)
{
    if (curr == nullptr)
        return;
    if (data &lt; curr-&gt;data)
        _del(curr-&gt;left, data);
    else if (data &gt; curr-&gt;data)
        _del(curr-&gt;right, data);
    else {
        // if one child is nullptr or both child are nullptr
        if (curr-&gt;left == nullptr) {
            auto &amp;p = curr-&gt;right;
            curr.reset(p.release());
        }
        else if (curr-&gt;right == nullptr) {
            auto &amp;p = curr-&gt;left;
            curr.reset(p.release());
        }
        //if child is non leaf node    
        else {
            T temp = _findmin(curr-&gt;right);
            curr-&gt;data = temp;
            _del(curr-&gt;right, temp);
        }
    }
}
template&lt;typename T&gt;
void Btree&lt;T&gt;::_traverse(std::unique_ptr&lt;node&gt;&amp; curr, void (*func)(T data))
{
    if (curr == nullptr)
        return;
    _traverse(curr-&gt;left, func);
    func(curr-&gt;data);
    _traverse(curr-&gt;right, func);
}
Observations
You pass data by value. This is fine for small data types like int. But imagine that T is some huge object that is very expensive to copy. You should think of passing the data by reference to avoid any intermediate copies. When you get a bit more advanced think about passing by r-value reference to allow you to move the objects.
For your traversal you pass a function pointer. This is a bit limiting. Normally you would templateze the function to allow you to pass any function like object (function pointer/functor/lambda/std::function) etc.
Yes you need recursion when traversing trees. But you don't need it everywhere. There are a couple of places where a simple loop would be sufficient.
I can see why you use std::unique_ptr. But in my view a tree is a container and should manage its own memory. So I would have simply used Node* inside data. The code is not that much harder to write in this context. BUT I don't have any real issue with std::unique_ptr.
Code
Insert by const reference
void insert(T const&amp; data)
{
    _insert(root, data);
}
_insert(std::unique_ptr&lt;node&gt;&amp; curr, T const&amp; data)
{
    if (curr.get() == nullptr) {
        curr.reset(new node(data));
        return;
    }

    auto&amp; next = (data &lt; curr-&gt;data) ? curr-&gt;left : curr-&gt;right;
    _insert(next, data);
}

Insert by r-Value reference
void insert(T&amp;&amp; data)
{
    _insert(root, std::forward&lt;T&gt;(data));
}
_insert(std::unique_ptr&lt;node&gt;&amp; curr, T&amp;&amp; data)
{
    if (curr.get() == nullptr) {
        curr.reset(new node(data));
        return;
    }

    auto&amp; next = (data &lt; curr-&gt;data) ? curr-&gt;left : curr-&gt;right;
    _insert(next, std::forward&lt;T&gt;(data));
}

Emplace into node.
// This calls T constructor only when you construct the node itself.
template&lt;typename... Args&gt;
void emplace(Args const&amp;... args)
{
    _emplace(root, args...));
}
template&lt;typename... Args&gt;
_emplace(std::unique_ptr&lt;node&gt;&amp; curr, Args const&amp;... args)
{
    if (curr.get() == nullptr) {
        curr.reset(new node(args...));
        return;
    }

    auto&amp; next = (data &lt; curr-&gt;data) ? curr-&gt;left : curr-&gt;right;
    _insert(next, args...);
}
template&lt;typename... Args&gt;
node::node(Args const&amp;... args)
    : data(args...)  // Data of type T constructed in place
    , left(nullptr)
    , right(nullptr)
{}

Using a function like object to traverse the tree:
template&lt;typename F&gt;
void traverse(const &amp; action)
{
    _traverse(root, action);
}
template&lt;typename F&gt;
void _traverse(std::unique_ptr&lt;node&gt;&amp; curr, F const&amp; action);


// outside class

template&lt;typename T&gt;
template&lt;typename F&gt;
void Btree&lt;T&gt;::_traverse(std::unique_ptr&lt;node&gt;&amp; curr, F const&amp; action)
{
    if (curr == nullptr)
        return;
    _traverse(curr-&gt;left, action);
    action(curr-&gt;data);
    _traverse(curr-&gt;right, action);
}

int main()
{
     Btree&lt;int&gt;  tree;
     tree.traverse([](int x){std::cout &lt;&lt; x &lt;&lt; &quot; &quot;;});
}