I ran into the following oddity when making a mistake writing some code for trees. I've stripped down this example a lot so it is only a Linear Tree.
Basically, in the main() function, I wanted to attach a Node to my tree, but instead of attaching it to "tree.root", I attached it to just "root". However, to my surprise, not only did it all compile just fine, but I was able to call methods on the nodes. It only errored when I tried to access the "value" member variable.
I guess my main question is, why didn't the compiler catch this bug?
std::shared_ptr<Node> root = tree.AddLeaf(12, root);
Since "root" on the RHS is a flat-out undeclared variable. Also, out of curiosity, if the compiler lets them through, do circular definitions have an actual use case? Here's the rest of the code:
#include <iostream> #include <memory> struct Node { int value; std::shared_ptr<Node> child; Node(int value) : value {value}, child {nullptr} {} int SubtreeDepth() { int current_depth = 1; if(child != nullptr) return current_depth + child->SubtreeDepth(); return current_depth; } }; struct Tree { std::shared_ptr<Node> root; std::shared_ptr<Node> AddLeaf(int value, std::shared_ptr<Node>& ptr) { if(ptr == nullptr) { ptr = std::move(std::make_shared<Node>(value)); return ptr; } else { std::shared_ptr<Node> newLeaf = std::make_shared<Node>(value); ptr->child = std::move(newLeaf); return ptr->child; } } }; int main(int argc, char * argv[]) { Tree tree; std::shared_ptr<Node> root = tree.AddLeaf(12, root); std::shared_ptr<Node> child = tree.AddLeaf(16, root); std::cout << "root->SubtreeDepth() = " << root->SubtreeDepth() << std::endl; std::cout << "child->SubtreeDepth() = " << child->SubtreeDepth() << std::endl; return 0; }
Output:
root->SubtreeDepth() = 2 child->SubtreeDepth() = 1
A circular reference in computer programming is a situation in which two values reference each other. This type of reference can be extremely problematic if two functions or values depend on one another for definition.
A circular definition is a description that uses the term(s) being defined as part of the description or assumes that the term(s) being described are already known.
That's an unfortunate side-effect of definitions in C++, that declaration and definition is done as separate steps. Because the variables are declared first, they can be used in their own initialization:
std::shared_ptr<Node> root = tree.AddLeaf(12, root); ^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^ Declaration of the variable Initialization clause of variable
Once the variable is declared, it can be used in the initialization for the full definition of itself.
It will lead to undefined behavior in AddLeaf
if the data of the second argument is used, as the variable is not initialized.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With