C++ Compiler allows circular definition?












17














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









share|improve this question


















  • 7




    int x = x + 1; what bug? What did you expect to happen?
    – Kamil Cuk
    yesterday










  • First the variable is initialized (int std::shared_ptr's case with null pointer), then the assignment is performed. Try using a constructor and it will not be allowed.
    – rkapl
    yesterday






  • 1




    @rkapl no, the variable is not initialized in this case until RHS completes. This statement is known as copy initialization.
    – Ruslan
    17 hours ago
















17














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









share|improve this question


















  • 7




    int x = x + 1; what bug? What did you expect to happen?
    – Kamil Cuk
    yesterday










  • First the variable is initialized (int std::shared_ptr's case with null pointer), then the assignment is performed. Try using a constructor and it will not be allowed.
    – rkapl
    yesterday






  • 1




    @rkapl no, the variable is not initialized in this case until RHS completes. This statement is known as copy initialization.
    – Ruslan
    17 hours ago














17












17








17


2





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









share|improve this question













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






c++ c++14






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked yesterday









user2520385user2520385

794516




794516








  • 7




    int x = x + 1; what bug? What did you expect to happen?
    – Kamil Cuk
    yesterday










  • First the variable is initialized (int std::shared_ptr's case with null pointer), then the assignment is performed. Try using a constructor and it will not be allowed.
    – rkapl
    yesterday






  • 1




    @rkapl no, the variable is not initialized in this case until RHS completes. This statement is known as copy initialization.
    – Ruslan
    17 hours ago














  • 7




    int x = x + 1; what bug? What did you expect to happen?
    – Kamil Cuk
    yesterday










  • First the variable is initialized (int std::shared_ptr's case with null pointer), then the assignment is performed. Try using a constructor and it will not be allowed.
    – rkapl
    yesterday






  • 1




    @rkapl no, the variable is not initialized in this case until RHS completes. This statement is known as copy initialization.
    – Ruslan
    17 hours ago








7




7




int x = x + 1; what bug? What did you expect to happen?
– Kamil Cuk
yesterday




int x = x + 1; what bug? What did you expect to happen?
– Kamil Cuk
yesterday












First the variable is initialized (int std::shared_ptr's case with null pointer), then the assignment is performed. Try using a constructor and it will not be allowed.
– rkapl
yesterday




First the variable is initialized (int std::shared_ptr's case with null pointer), then the assignment is performed. Try using a constructor and it will not be allowed.
– rkapl
yesterday




1




1




@rkapl no, the variable is not initialized in this case until RHS completes. This statement is known as copy initialization.
– Ruslan
17 hours ago




@rkapl no, the variable is not initialized in this case until RHS completes. This statement is known as copy initialization.
– Ruslan
17 hours ago












2 Answers
2






active

oldest

votes


















20














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.






share|improve this answer























  • In this case - the default constructor of root would be called first; then addLeaf; then equals operator. I don't think there's any UB here.
    – UKMonkey
    yesterday






  • 7




    @UKMonkey Note that this isn't assignment, but copy-construction (or rather copy initialization). The definition is equal to std::shared_ptr<Node> root(tree.AddLeaf(12, root)); The default constructor will not be called.
    – Some programmer dude
    yesterday








  • 3




    @UKMonkey: Compilers have no choice in this matter. The Standard strictly defines overload resolution for constructors, and the number of arguments alone rules out the default constructor. Efficiency is not a factor in that.
    – MSalters
    yesterday






  • 6




    It only leads to undefined behavior if the variable is read from or written to. If its address or identity is stored but the value of the variable is never read from or written to, no UB occurs. It is, however, very fragile.
    – Yakk - Adam Nevraumont
    23 hours ago






  • 3




    At least in C (can't think of any good reason in c++ though except the original compatibility with c) there's good reason for having definition and declaration separately though: Foo *foo = malloc(sizeof(*foo)); is a common idiom and avoids common pitfalls when refactoring code.
    – Voo
    18 hours ago





















12















Since "root" on the RHS is a flat-out undeclared variable.




It's not undeclared. It is declared by that very same statement. However, root is uninitialised at the point where AddLeaf(root) is called, so when the value of the object is used (compared to null etc.) within the function, the behaviour is undefined.



Yes, using a variable in its own declaration is allowed, but using its value is not. Pretty much all you can do with it is take the address or create a reference.



Yes, there are use cases although they may be rare. For example, you might want to represent a graph, and you might have a constructor for node that takes a pointer to linked node as an argument and you might want to be able to represent a node that links with itself. Thus you might write Node n(&n). I won't argue whether that would be a good design for a graph API.






share|improve this answer























    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54073039%2fc-compiler-allows-circular-definition%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    2 Answers
    2






    active

    oldest

    votes








    2 Answers
    2






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    20














    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.






    share|improve this answer























    • In this case - the default constructor of root would be called first; then addLeaf; then equals operator. I don't think there's any UB here.
      – UKMonkey
      yesterday






    • 7




      @UKMonkey Note that this isn't assignment, but copy-construction (or rather copy initialization). The definition is equal to std::shared_ptr<Node> root(tree.AddLeaf(12, root)); The default constructor will not be called.
      – Some programmer dude
      yesterday








    • 3




      @UKMonkey: Compilers have no choice in this matter. The Standard strictly defines overload resolution for constructors, and the number of arguments alone rules out the default constructor. Efficiency is not a factor in that.
      – MSalters
      yesterday






    • 6




      It only leads to undefined behavior if the variable is read from or written to. If its address or identity is stored but the value of the variable is never read from or written to, no UB occurs. It is, however, very fragile.
      – Yakk - Adam Nevraumont
      23 hours ago






    • 3




      At least in C (can't think of any good reason in c++ though except the original compatibility with c) there's good reason for having definition and declaration separately though: Foo *foo = malloc(sizeof(*foo)); is a common idiom and avoids common pitfalls when refactoring code.
      – Voo
      18 hours ago


















    20














    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.






    share|improve this answer























    • In this case - the default constructor of root would be called first; then addLeaf; then equals operator. I don't think there's any UB here.
      – UKMonkey
      yesterday






    • 7




      @UKMonkey Note that this isn't assignment, but copy-construction (or rather copy initialization). The definition is equal to std::shared_ptr<Node> root(tree.AddLeaf(12, root)); The default constructor will not be called.
      – Some programmer dude
      yesterday








    • 3




      @UKMonkey: Compilers have no choice in this matter. The Standard strictly defines overload resolution for constructors, and the number of arguments alone rules out the default constructor. Efficiency is not a factor in that.
      – MSalters
      yesterday






    • 6




      It only leads to undefined behavior if the variable is read from or written to. If its address or identity is stored but the value of the variable is never read from or written to, no UB occurs. It is, however, very fragile.
      – Yakk - Adam Nevraumont
      23 hours ago






    • 3




      At least in C (can't think of any good reason in c++ though except the original compatibility with c) there's good reason for having definition and declaration separately though: Foo *foo = malloc(sizeof(*foo)); is a common idiom and avoids common pitfalls when refactoring code.
      – Voo
      18 hours ago
















    20












    20








    20






    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.






    share|improve this answer














    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.







    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited 23 hours ago

























    answered yesterday









    Some programmer dudeSome programmer dude

    295k24249411




    295k24249411












    • In this case - the default constructor of root would be called first; then addLeaf; then equals operator. I don't think there's any UB here.
      – UKMonkey
      yesterday






    • 7




      @UKMonkey Note that this isn't assignment, but copy-construction (or rather copy initialization). The definition is equal to std::shared_ptr<Node> root(tree.AddLeaf(12, root)); The default constructor will not be called.
      – Some programmer dude
      yesterday








    • 3




      @UKMonkey: Compilers have no choice in this matter. The Standard strictly defines overload resolution for constructors, and the number of arguments alone rules out the default constructor. Efficiency is not a factor in that.
      – MSalters
      yesterday






    • 6




      It only leads to undefined behavior if the variable is read from or written to. If its address or identity is stored but the value of the variable is never read from or written to, no UB occurs. It is, however, very fragile.
      – Yakk - Adam Nevraumont
      23 hours ago






    • 3




      At least in C (can't think of any good reason in c++ though except the original compatibility with c) there's good reason for having definition and declaration separately though: Foo *foo = malloc(sizeof(*foo)); is a common idiom and avoids common pitfalls when refactoring code.
      – Voo
      18 hours ago




















    • In this case - the default constructor of root would be called first; then addLeaf; then equals operator. I don't think there's any UB here.
      – UKMonkey
      yesterday






    • 7




      @UKMonkey Note that this isn't assignment, but copy-construction (or rather copy initialization). The definition is equal to std::shared_ptr<Node> root(tree.AddLeaf(12, root)); The default constructor will not be called.
      – Some programmer dude
      yesterday








    • 3




      @UKMonkey: Compilers have no choice in this matter. The Standard strictly defines overload resolution for constructors, and the number of arguments alone rules out the default constructor. Efficiency is not a factor in that.
      – MSalters
      yesterday






    • 6




      It only leads to undefined behavior if the variable is read from or written to. If its address or identity is stored but the value of the variable is never read from or written to, no UB occurs. It is, however, very fragile.
      – Yakk - Adam Nevraumont
      23 hours ago






    • 3




      At least in C (can't think of any good reason in c++ though except the original compatibility with c) there's good reason for having definition and declaration separately though: Foo *foo = malloc(sizeof(*foo)); is a common idiom and avoids common pitfalls when refactoring code.
      – Voo
      18 hours ago


















    In this case - the default constructor of root would be called first; then addLeaf; then equals operator. I don't think there's any UB here.
    – UKMonkey
    yesterday




    In this case - the default constructor of root would be called first; then addLeaf; then equals operator. I don't think there's any UB here.
    – UKMonkey
    yesterday




    7




    7




    @UKMonkey Note that this isn't assignment, but copy-construction (or rather copy initialization). The definition is equal to std::shared_ptr<Node> root(tree.AddLeaf(12, root)); The default constructor will not be called.
    – Some programmer dude
    yesterday






    @UKMonkey Note that this isn't assignment, but copy-construction (or rather copy initialization). The definition is equal to std::shared_ptr<Node> root(tree.AddLeaf(12, root)); The default constructor will not be called.
    – Some programmer dude
    yesterday






    3




    3




    @UKMonkey: Compilers have no choice in this matter. The Standard strictly defines overload resolution for constructors, and the number of arguments alone rules out the default constructor. Efficiency is not a factor in that.
    – MSalters
    yesterday




    @UKMonkey: Compilers have no choice in this matter. The Standard strictly defines overload resolution for constructors, and the number of arguments alone rules out the default constructor. Efficiency is not a factor in that.
    – MSalters
    yesterday




    6




    6




    It only leads to undefined behavior if the variable is read from or written to. If its address or identity is stored but the value of the variable is never read from or written to, no UB occurs. It is, however, very fragile.
    – Yakk - Adam Nevraumont
    23 hours ago




    It only leads to undefined behavior if the variable is read from or written to. If its address or identity is stored but the value of the variable is never read from or written to, no UB occurs. It is, however, very fragile.
    – Yakk - Adam Nevraumont
    23 hours ago




    3




    3




    At least in C (can't think of any good reason in c++ though except the original compatibility with c) there's good reason for having definition and declaration separately though: Foo *foo = malloc(sizeof(*foo)); is a common idiom and avoids common pitfalls when refactoring code.
    – Voo
    18 hours ago






    At least in C (can't think of any good reason in c++ though except the original compatibility with c) there's good reason for having definition and declaration separately though: Foo *foo = malloc(sizeof(*foo)); is a common idiom and avoids common pitfalls when refactoring code.
    – Voo
    18 hours ago















    12















    Since "root" on the RHS is a flat-out undeclared variable.




    It's not undeclared. It is declared by that very same statement. However, root is uninitialised at the point where AddLeaf(root) is called, so when the value of the object is used (compared to null etc.) within the function, the behaviour is undefined.



    Yes, using a variable in its own declaration is allowed, but using its value is not. Pretty much all you can do with it is take the address or create a reference.



    Yes, there are use cases although they may be rare. For example, you might want to represent a graph, and you might have a constructor for node that takes a pointer to linked node as an argument and you might want to be able to represent a node that links with itself. Thus you might write Node n(&n). I won't argue whether that would be a good design for a graph API.






    share|improve this answer




























      12















      Since "root" on the RHS is a flat-out undeclared variable.




      It's not undeclared. It is declared by that very same statement. However, root is uninitialised at the point where AddLeaf(root) is called, so when the value of the object is used (compared to null etc.) within the function, the behaviour is undefined.



      Yes, using a variable in its own declaration is allowed, but using its value is not. Pretty much all you can do with it is take the address or create a reference.



      Yes, there are use cases although they may be rare. For example, you might want to represent a graph, and you might have a constructor for node that takes a pointer to linked node as an argument and you might want to be able to represent a node that links with itself. Thus you might write Node n(&n). I won't argue whether that would be a good design for a graph API.






      share|improve this answer


























        12












        12








        12







        Since "root" on the RHS is a flat-out undeclared variable.




        It's not undeclared. It is declared by that very same statement. However, root is uninitialised at the point where AddLeaf(root) is called, so when the value of the object is used (compared to null etc.) within the function, the behaviour is undefined.



        Yes, using a variable in its own declaration is allowed, but using its value is not. Pretty much all you can do with it is take the address or create a reference.



        Yes, there are use cases although they may be rare. For example, you might want to represent a graph, and you might have a constructor for node that takes a pointer to linked node as an argument and you might want to be able to represent a node that links with itself. Thus you might write Node n(&n). I won't argue whether that would be a good design for a graph API.






        share|improve this answer















        Since "root" on the RHS is a flat-out undeclared variable.




        It's not undeclared. It is declared by that very same statement. However, root is uninitialised at the point where AddLeaf(root) is called, so when the value of the object is used (compared to null etc.) within the function, the behaviour is undefined.



        Yes, using a variable in its own declaration is allowed, but using its value is not. Pretty much all you can do with it is take the address or create a reference.



        Yes, there are use cases although they may be rare. For example, you might want to represent a graph, and you might have a constructor for node that takes a pointer to linked node as an argument and you might want to be able to represent a node that links with itself. Thus you might write Node n(&n). I won't argue whether that would be a good design for a graph API.







        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited yesterday

























        answered yesterday









        eerorikaeerorika

        77.1k556118




        77.1k556118






























            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.





            Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


            Please pay close attention to the following guidance:


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54073039%2fc-compiler-allows-circular-definition%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            How to make a Squid Proxy server?

            Is this a new Fibonacci Identity?

            19世紀