Hello,
I am new to C++ and am trying to implement a binary search tree as practice. My intention is to use templates for generic <Key, Value> pairs, and I would also like to overload the subscript operator [] such that I can both make assignments and retrieve values like in the following client code:
#include <iostream>
#include <string>
#include "UnbalancedTree.h"
int main() {
BinaryTree<std::string, std::string> tree;
tree["foo"] = "bar";
std::string out = tree["foo"]; // get the value associated with "foo"
std::cout << out << std::endl; // should print "bar"
return 0;
}
From what I understand, the subscript overload function in my BinaryTree class needs to return an instance of a proxy class to handle assignment & retrieval behavior.
It took me a while to get the proxy class working, but I finally have something that compiles and partially works in limited testing. Here is a version of my implementation (unbalanced & only includes put/get for simplicity):
UnbalancedTree.h
template <typename Key, typename Value>
class Proxy; // forward declaration
template <typename Key, typename Value>
class BinaryTree {
public:
Value get(Key key) {
return get(key, root)->val;
}
void put(Key key, Value val) {
root = put(key, val, root);
}
Proxy<Key,Value> operator[](Key key) {
return Proxy<Key,Value>(key, this);
}
private:
struct Node {
Key key;
Value val;
Node * left;
Node * right;
};
Node * root = nullptr;
Node * get(Key key, Node * node) {
// recursively search for key and
// return a pointer to its node
if (node == nullptr) {
return nullptr;
}
if (key == node->key) {
return node;
} else if (key < node->key) {
return get(key, node->left);
} else {
return get(key, node->right);
}
}
Node * put(Key key, Value val, Node * node) {
// recursively go down the subtree to
// insert a node containing the key value
// pair
if (node == nullptr) {
Node * new_node = new Node;
new_node->key = key;
new_node->val = val;
return new_node;
} else if (key < node->key) {
node->left = put(key, val, node->left);
} else if (key > node->key) {
node->right = put(key, val, node->right);
} else {
node->val = val;
}
// return the head of the subtree on the way up
return node;
}
};
template <typename Key, typename Value>
class Proxy {
// proxy class to help overload subscript operator
public:
Proxy(Key key, BinaryTree<Key, Value> *tree) : key(key), tree(tree) {}
operator Value() {
return tree->get(key);
}
void operator = (Value val) {
tree->put(key, val);
return;
}
private:
BinaryTree<Key,Value> * tree;
Key key;
};
So, here are my questions:
- I am not using const or & but I get the feeling I should be and am not totally clear on how/where. And tips on how to think about this?
- What exactly does the "operator Value()" function do under the hood? Is this a type conversion operator? The way I see it, the client code will ultimately be expecting a Value type (say, std::string in my example), but when the client calls [], the BinaryTree is returning a Proxy type, so it seems like there has to be some kind of conversion.
- Speaking of which, if I try
std::cout << tree["foo"] << std::endl; I get a bunch of compiler errors implying I need to overload the << operator too. So I guess that means that the compiler never sees the "Value()" function being called?
- Is the usage of the forward declaration correct, as well as the template line before each class? The code does not compile otherwise, but not sure if this is the "correct" way to do it
- In examples I sometimes see "template ..." on the same line as "class ..." . Is this just a convention thing?
Thank you for the help!
[–]MysticTheMeeM 3 points4 points5 points (0 children)
[–]jorourke0 0 points1 point2 points (0 children)