I've implemented a simple binary search tree for practice purposes:
#ifndef BST_H
#define BST_H
template <typename T>
class treeNode {
public:
    treeNode *left;
    treeNode *right;
    T key;
    treeNode(T key)
        : key(key)
        , left(nullptr)
        , right(nullptr) {
    }
};
template <typename T>
class BST {
public:
    BST() {
        root = nullptr;
        nodes = 0;
    }
    BST(BST const& rhs)
        : nodes(rhs.size()) {
        // not yet implemented
    }
    BST& operator = (BST rhs) {
        this->swap(rhs);
    }
    BST& operator = (BST&& rhs) {
        this->swap(rhs);
    }
    ~BST() {
        clear(root);
    }
    void swap(BST& other) {
        std::swap(root, other.root);
        std::swap(nodes, other.nodes);
    }
    void clear(treeNode<T>* node) {
        if(node) {
            if(node->left) clear(node->left);
            if(node->right) clear(node->right);
            delete node;
        }
    }
    bool isEmpty() const {
        return root == nullptr;
    }
    void inorder(treeNode<T>*);
    void traverseInorder();
    void preorder(treeNode<T>*);
    void traversePreorder();
    void postorder(treeNode<T>*);
    void traversePostorder();
    void insert(T const& );
    void remove(T const& );
    treeNode<T>* search(const T &);
    treeNode<T>* minHelper(treeNode<T>*);
    treeNode<T>* min();
    treeNode<T>* maxHelper(treeNode<T>*);
    treeNode<T>* max();
    size_t size() const;
    void sort();
    treeNode<T>* inOrderSuccessor(treeNode<T>*);
    bool isBST(treeNode<T>*) const;
    bool isBST() const;
private:
    treeNode<T> *root;
    size_t nodes;
};
// Smaller elements go left
// larger elements go right
template <class T>
void BST<T>::insert(T const& value) {
    treeNode<T> *newNode = new treeNode<T>(value);
    treeNode<T> *parent = nullptr;
    // is this a new tree?
    if(isEmpty()) {
        root = newNode;
        ++nodes;
        return;
    }
    //Note: ALL insertions are as leaf nodes
    treeNode<T>* curr = root;
    // Find the Node's parent
    while(curr) {
        parent = curr;
        curr = newNode->key > curr->key ? curr->right : curr->left;
    }
    if(newNode->key < parent->key)
        parent->left = newNode;
    else
        parent->right = newNode;
    ++nodes;
}
template <typename T>
void BST<T>::remove(T const& data) {
    if(isEmpty()) {
        throw std::runtime_error("Invalid Action!");
    }
    treeNode<T> *curr = root;
    treeNode<T> *parent;
    // root to leaf search (top-down)
    while(curr) {
        if(curr->key == data) {
            break;
        } else {
            parent = curr;
            curr = data > curr->key ? curr->right : curr->left;
        }
    }
    if(curr == nullptr) {
        cout << "key not found! " << endl;
        return;
    }
    // 3 cases :
    // 1. We're removing a leaf node
    // 2. We're removing a node with a single child
    // 3. we're removing a node with 2 children
    //We're looking at a leaf node
    if( curr->left == nullptr and curr->right == nullptr) {
        if(parent->left == curr)
            parent->left = nullptr;
        else
            parent->right = nullptr;
        delete curr;
        --nodes;
        return;
    }
    // Node with single child
    if((curr->left == nullptr and curr->right != nullptr) or (curr->left != nullptr and curr->right == nullptr)) {
        if(curr->left == nullptr and curr->right != nullptr) {
            if(parent->left == curr) {
                parent->left = curr->right;
            } else {
                parent->right = curr->right;
            }
        } else { // left child present, no right child
            if(parent->left == curr) {
                parent->left = curr->left;
            } else {
                parent->right = curr->left;
            }
        }
        delete curr;
        --nodes;
        return;
    }
    // Node with 2 children
    // replace node with smallest value in right subtree
    if (curr->left != nullptr and curr->right != nullptr) {
        treeNode<T> *curr_right = curr->right;
        if(curr_right->left == nullptr and curr_right->right == nullptr) {
            curr->key = curr_right->key;
            delete curr_right;
            curr->right = nullptr;
        } else { // right child has children
            //if the node's right child has a left child
            // Move all the way down left to locate smallest element
            if((curr->right)->left != nullptr) {
                treeNode<T>* lcurr;
                treeNode<T>* lcurr_parent;
                lcurr_parent = curr->right;
                lcurr = (curr->right)->left;
                while(lcurr->left != nullptr) {
                    lcurr_parent = lcurr;
                    lcurr = lcurr->left;
                }
                curr->key = lcurr->key;
                delete lcurr;
                lcurr_parent->left = nullptr;
            } else { // (curr->right)->right != nullptr
                treeNode<T> *tmp = curr->right;
                curr->key = tmp->key;
                curr->right = tmp->right;
                delete tmp;
            }
        }
        --nodes;
    }
}
template <typename T>
treeNode<T>* BST<T> :: search(T const& value) {
    treeNode<T> *curr = root;
    while (curr) {
        if(curr->key == value) {
            return curr;
        } else if(value < curr->key) {
            curr = curr->left;
        } else curr = curr->right;
    }
    return nullptr;
}
template <typename T>
treeNode<T>* BST <T> :: minHelper(treeNode<T>* node) {
    if(node->left == nullptr)
        return node;
    minHelper(node->left);
}
template <typename T>
treeNode<T>* BST <T> :: min() {
    return minHelper(root);
}
template <typename T>
treeNode<T>* BST <T> :: maxHelper(treeNode<T>* node) {
    if(node->right == nullptr)
        return node;
    maxHelper(node->right);
}
template <typename T>
treeNode<T>* BST <T> :: max() {
    return maxHelper(root);
}
template<typename T>
size_t BST<T>::size() const {
    return nodes;
}
template<typename T>
void BST<T>::inorder(treeNode<T>* node) {
    if(node != nullptr) {
        if(node->left) inorder(node->left);
        cout << " " << node->key << " ";
        if(node->right)
            inorder(node->right);
    }
}
template<typename T>
void BST<T>::traverseInorder() {
    inorder(root);
}
template<typename T>
void BST<T>::sort() {
    inorder(root);
}
template<typename T>
void BST<T>::preorder(treeNode<T>* node) {
    if(node != nullptr) {
        cout << " " << node->key << " ";
        if(node->left) preorder(node->left);
        if(node->right) preorder(node->right);
    }
}
template<typename T>
void BST<T>::traversePreorder() {
    preorder(root);
}
template<typename T>
void BST<T>::postorder(treeNode<T>* node) {
    if(node != nullptr) {
        if(node->left) postorder(node->left);
        if(node->right) postorder(node->right);
        cout << " " << node->key << " ";
    }
}
template<typename T>
void BST<T>::traversePostorder() {
    postorder(root);
}
template <class T>
treeNode<T>* BST<T> :: inOrderSuccessor(treeNode<T>* node) {
    if(node->right != nullptr)
        return minHelper(node->right);
    treeNode<T>* succ = nullptr;
    treeNode<T>* curr = root;
    // Start from root and search for successor down the tree
    while (curr != nullptr) {
        if (node->key < curr->key) {
            succ = curr;
            curr = curr->left;
        } else if (node->key > curr->key)
            curr = curr->right;
        else
            break;
    }
    return succ;
}
template<typename T>
bool BST<T>::isBST(treeNode<T>* node) const {
    static struct treeNode<T> *prev = nullptr;
    // traverse the tree in inorder fashion and keep track of prev node
    if (node) {
        if (!isBST(node->left))
            return false;
        // Allows only distinct valued nodes
        if (prev != nullptr and node->key <= prev->key)
            return false;
        prev = node;
        return isBST(node->right);
    }
    return true;
}
template<typename T>
bool BST<T>::isBST() const {
    return isBST(root);
}
#endif // BST_H
Obviously it's not a complete implementation. I would appreciate all criticism relevant to code, style, flow, camelCase vs underscore, and so forth.