C++ Compiler allows circular definition?

  • A+
Category:Languages

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 

 


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 leads to undefined behavior as the variable is not initialized.

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: