What happens when a constructor function calls itself in VS2013?












14















class IA
{
public:
virtual void Print() = 0;
}
IA* GetA();

class A : public IA
{
public:
int num = 10;
A()
{
GetA()->Print();
}
void Print()
{
std::cout << num << std::endl;
}
}

IA* GetA()
{
static A a;
return &a;
}

int main()
{
GetA();
std::cout << "End" << std::endl;
getchar();
return 0;
}



  1. Obviously, class A's constructor function calls itself.

  2. "static A a" will get stuck in a loop.

  3. On VS2013, this code can get out from the loop and print "End" on the console.


  4. On VS2017, this code gets stuck in a loop.



    **What does VS2013 do for this code?












share|improve this question

























  • You discovered differences between two different VS versions - but the nature of the problem has wider scope (what would GCC, clang, ... do?), so I recommend dropping the two VS tags (3. and 4. remaining just examples, you even might ask in 5. for other compilers...).

    – Aconcagua
    Jan 14 at 7:43






  • 2





    Visual Studio is not a compiler. It's an IDE that invokes compilers.

    – jpmc26
    Jan 14 at 10:44
















14















class IA
{
public:
virtual void Print() = 0;
}
IA* GetA();

class A : public IA
{
public:
int num = 10;
A()
{
GetA()->Print();
}
void Print()
{
std::cout << num << std::endl;
}
}

IA* GetA()
{
static A a;
return &a;
}

int main()
{
GetA();
std::cout << "End" << std::endl;
getchar();
return 0;
}



  1. Obviously, class A's constructor function calls itself.

  2. "static A a" will get stuck in a loop.

  3. On VS2013, this code can get out from the loop and print "End" on the console.


  4. On VS2017, this code gets stuck in a loop.



    **What does VS2013 do for this code?












share|improve this question

























  • You discovered differences between two different VS versions - but the nature of the problem has wider scope (what would GCC, clang, ... do?), so I recommend dropping the two VS tags (3. and 4. remaining just examples, you even might ask in 5. for other compilers...).

    – Aconcagua
    Jan 14 at 7:43






  • 2





    Visual Studio is not a compiler. It's an IDE that invokes compilers.

    – jpmc26
    Jan 14 at 10:44














14












14








14








class IA
{
public:
virtual void Print() = 0;
}
IA* GetA();

class A : public IA
{
public:
int num = 10;
A()
{
GetA()->Print();
}
void Print()
{
std::cout << num << std::endl;
}
}

IA* GetA()
{
static A a;
return &a;
}

int main()
{
GetA();
std::cout << "End" << std::endl;
getchar();
return 0;
}



  1. Obviously, class A's constructor function calls itself.

  2. "static A a" will get stuck in a loop.

  3. On VS2013, this code can get out from the loop and print "End" on the console.


  4. On VS2017, this code gets stuck in a loop.



    **What does VS2013 do for this code?












share|improve this question
















class IA
{
public:
virtual void Print() = 0;
}
IA* GetA();

class A : public IA
{
public:
int num = 10;
A()
{
GetA()->Print();
}
void Print()
{
std::cout << num << std::endl;
}
}

IA* GetA()
{
static A a;
return &a;
}

int main()
{
GetA();
std::cout << "End" << std::endl;
getchar();
return 0;
}



  1. Obviously, class A's constructor function calls itself.

  2. "static A a" will get stuck in a loop.

  3. On VS2013, this code can get out from the loop and print "End" on the console.


  4. On VS2017, this code gets stuck in a loop.



    **What does VS2013 do for this code?









c++ visual-studio-2013 visual-studio-2017






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Jan 14 at 14:41









Boann

36.8k1288121




36.8k1288121










asked Jan 14 at 7:13









MiCMiC

856




856













  • You discovered differences between two different VS versions - but the nature of the problem has wider scope (what would GCC, clang, ... do?), so I recommend dropping the two VS tags (3. and 4. remaining just examples, you even might ask in 5. for other compilers...).

    – Aconcagua
    Jan 14 at 7:43






  • 2





    Visual Studio is not a compiler. It's an IDE that invokes compilers.

    – jpmc26
    Jan 14 at 10:44



















  • You discovered differences between two different VS versions - but the nature of the problem has wider scope (what would GCC, clang, ... do?), so I recommend dropping the two VS tags (3. and 4. remaining just examples, you even might ask in 5. for other compilers...).

    – Aconcagua
    Jan 14 at 7:43






  • 2





    Visual Studio is not a compiler. It's an IDE that invokes compilers.

    – jpmc26
    Jan 14 at 10:44

















You discovered differences between two different VS versions - but the nature of the problem has wider scope (what would GCC, clang, ... do?), so I recommend dropping the two VS tags (3. and 4. remaining just examples, you even might ask in 5. for other compilers...).

– Aconcagua
Jan 14 at 7:43





You discovered differences between two different VS versions - but the nature of the problem has wider scope (what would GCC, clang, ... do?), so I recommend dropping the two VS tags (3. and 4. remaining just examples, you even might ask in 5. for other compilers...).

– Aconcagua
Jan 14 at 7:43




2




2





Visual Studio is not a compiler. It's an IDE that invokes compilers.

– jpmc26
Jan 14 at 10:44





Visual Studio is not a compiler. It's an IDE that invokes compilers.

– jpmc26
Jan 14 at 10:44












2 Answers
2






active

oldest

votes


















18














Nothing in particular has to happen. According to the C++ standard:




[stmt.dcl] (emphasis mine)



4 Dynamic initialization of a block-scope variable with static storage
duration or thread storage duration is performed the first time
control passes through its declaration; such a variable is considered
initialized upon the completion of its initialization. If the
initialization exits by throwing an exception, the initialization is
not complete, so it will be tried again the next time control enters
the declaration. If control enters the declaration concurrently while
the variable is being initialized, the concurrent execution shall wait
for completion of the initialization. If control re-enters the
declaration recursively while the variable is being initialized, the
behavior is undefined.
[ Example:



int foo(int i) {
static int s = foo(2*i); // recursive call - undefined
return i+1;
}


 — end example ]




The statement I emboldened is exactly what happens in your program. It's also what the standard's example shows as undefined. The language specification says an implementation can do whatever it deems appropriate. So it could cause an infinite loop, or it may not, depending on the synchronization primitives your implementation uses to prevent concurrent reentry into the block (the initialization has to be thread safe).



Even before C++11, the behavior of recursive reentry was undefined. An implementation could do anything to make sure an object is initialized only once, which in turn could produce different results.



But you can't expect anything specific to happen portably. Not to mention undefined behavior always leaves room for a small chance of nasal demons.






share|improve this answer


























  • 'infinite recursion' would be the better term - and at least in theory, it offers another interesting outcome: crash due to stack overflow (if TCO is not applied)...

    – Aconcagua
    Jan 14 at 7:51











  • @Aconcagua - You say to-may-to, I say to-mah-to. Both are Turing equivalent after all ;). Just sticking to the OP's way of phrasing and thinking about it.

    – StoryTeller
    Jan 14 at 7:54






  • 2





    And don't forget the option of a deadlock should VS be using a non recursive synchronization primitive.

    – StoryTeller
    Jan 14 at 7:55



















4














The behaviour is undefined. The reason it "worked" in Visual Studio 2013 is that it didn't implement thread safe initialisation of function statics. What is probably happening is that the first call to GetA() creates a and calls the constructor. The second call to GetA() then just returns the partially constructed a. As the body of your constructor doesn't initialise anything calling Print() doesn't crash.



Visual Studio 2017 does implement thread safe initialisation. and presumably locks some mutex on entry to GetA() if a is not initialised, the second call to GetA() then encounters the locked mutex and deadlocks.



Note in both cases this is just my guess from the observed behaviour, the actual behaviour is undefined, for example GetA() may end up creating 2 instances of A.






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%2f54177087%2fwhat-happens-when-a-constructor-function-calls-itself-in-vs2013%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









    18














    Nothing in particular has to happen. According to the C++ standard:




    [stmt.dcl] (emphasis mine)



    4 Dynamic initialization of a block-scope variable with static storage
    duration or thread storage duration is performed the first time
    control passes through its declaration; such a variable is considered
    initialized upon the completion of its initialization. If the
    initialization exits by throwing an exception, the initialization is
    not complete, so it will be tried again the next time control enters
    the declaration. If control enters the declaration concurrently while
    the variable is being initialized, the concurrent execution shall wait
    for completion of the initialization. If control re-enters the
    declaration recursively while the variable is being initialized, the
    behavior is undefined.
    [ Example:



    int foo(int i) {
    static int s = foo(2*i); // recursive call - undefined
    return i+1;
    }


     — end example ]




    The statement I emboldened is exactly what happens in your program. It's also what the standard's example shows as undefined. The language specification says an implementation can do whatever it deems appropriate. So it could cause an infinite loop, or it may not, depending on the synchronization primitives your implementation uses to prevent concurrent reentry into the block (the initialization has to be thread safe).



    Even before C++11, the behavior of recursive reentry was undefined. An implementation could do anything to make sure an object is initialized only once, which in turn could produce different results.



    But you can't expect anything specific to happen portably. Not to mention undefined behavior always leaves room for a small chance of nasal demons.






    share|improve this answer


























    • 'infinite recursion' would be the better term - and at least in theory, it offers another interesting outcome: crash due to stack overflow (if TCO is not applied)...

      – Aconcagua
      Jan 14 at 7:51











    • @Aconcagua - You say to-may-to, I say to-mah-to. Both are Turing equivalent after all ;). Just sticking to the OP's way of phrasing and thinking about it.

      – StoryTeller
      Jan 14 at 7:54






    • 2





      And don't forget the option of a deadlock should VS be using a non recursive synchronization primitive.

      – StoryTeller
      Jan 14 at 7:55
















    18














    Nothing in particular has to happen. According to the C++ standard:




    [stmt.dcl] (emphasis mine)



    4 Dynamic initialization of a block-scope variable with static storage
    duration or thread storage duration is performed the first time
    control passes through its declaration; such a variable is considered
    initialized upon the completion of its initialization. If the
    initialization exits by throwing an exception, the initialization is
    not complete, so it will be tried again the next time control enters
    the declaration. If control enters the declaration concurrently while
    the variable is being initialized, the concurrent execution shall wait
    for completion of the initialization. If control re-enters the
    declaration recursively while the variable is being initialized, the
    behavior is undefined.
    [ Example:



    int foo(int i) {
    static int s = foo(2*i); // recursive call - undefined
    return i+1;
    }


     — end example ]




    The statement I emboldened is exactly what happens in your program. It's also what the standard's example shows as undefined. The language specification says an implementation can do whatever it deems appropriate. So it could cause an infinite loop, or it may not, depending on the synchronization primitives your implementation uses to prevent concurrent reentry into the block (the initialization has to be thread safe).



    Even before C++11, the behavior of recursive reentry was undefined. An implementation could do anything to make sure an object is initialized only once, which in turn could produce different results.



    But you can't expect anything specific to happen portably. Not to mention undefined behavior always leaves room for a small chance of nasal demons.






    share|improve this answer


























    • 'infinite recursion' would be the better term - and at least in theory, it offers another interesting outcome: crash due to stack overflow (if TCO is not applied)...

      – Aconcagua
      Jan 14 at 7:51











    • @Aconcagua - You say to-may-to, I say to-mah-to. Both are Turing equivalent after all ;). Just sticking to the OP's way of phrasing and thinking about it.

      – StoryTeller
      Jan 14 at 7:54






    • 2





      And don't forget the option of a deadlock should VS be using a non recursive synchronization primitive.

      – StoryTeller
      Jan 14 at 7:55














    18












    18








    18







    Nothing in particular has to happen. According to the C++ standard:




    [stmt.dcl] (emphasis mine)



    4 Dynamic initialization of a block-scope variable with static storage
    duration or thread storage duration is performed the first time
    control passes through its declaration; such a variable is considered
    initialized upon the completion of its initialization. If the
    initialization exits by throwing an exception, the initialization is
    not complete, so it will be tried again the next time control enters
    the declaration. If control enters the declaration concurrently while
    the variable is being initialized, the concurrent execution shall wait
    for completion of the initialization. If control re-enters the
    declaration recursively while the variable is being initialized, the
    behavior is undefined.
    [ Example:



    int foo(int i) {
    static int s = foo(2*i); // recursive call - undefined
    return i+1;
    }


     — end example ]




    The statement I emboldened is exactly what happens in your program. It's also what the standard's example shows as undefined. The language specification says an implementation can do whatever it deems appropriate. So it could cause an infinite loop, or it may not, depending on the synchronization primitives your implementation uses to prevent concurrent reentry into the block (the initialization has to be thread safe).



    Even before C++11, the behavior of recursive reentry was undefined. An implementation could do anything to make sure an object is initialized only once, which in turn could produce different results.



    But you can't expect anything specific to happen portably. Not to mention undefined behavior always leaves room for a small chance of nasal demons.






    share|improve this answer















    Nothing in particular has to happen. According to the C++ standard:




    [stmt.dcl] (emphasis mine)



    4 Dynamic initialization of a block-scope variable with static storage
    duration or thread storage duration is performed the first time
    control passes through its declaration; such a variable is considered
    initialized upon the completion of its initialization. If the
    initialization exits by throwing an exception, the initialization is
    not complete, so it will be tried again the next time control enters
    the declaration. If control enters the declaration concurrently while
    the variable is being initialized, the concurrent execution shall wait
    for completion of the initialization. If control re-enters the
    declaration recursively while the variable is being initialized, the
    behavior is undefined.
    [ Example:



    int foo(int i) {
    static int s = foo(2*i); // recursive call - undefined
    return i+1;
    }


     — end example ]




    The statement I emboldened is exactly what happens in your program. It's also what the standard's example shows as undefined. The language specification says an implementation can do whatever it deems appropriate. So it could cause an infinite loop, or it may not, depending on the synchronization primitives your implementation uses to prevent concurrent reentry into the block (the initialization has to be thread safe).



    Even before C++11, the behavior of recursive reentry was undefined. An implementation could do anything to make sure an object is initialized only once, which in turn could produce different results.



    But you can't expect anything specific to happen portably. Not to mention undefined behavior always leaves room for a small chance of nasal demons.







    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited Jan 14 at 8:45

























    answered Jan 14 at 7:27









    StoryTellerStoryTeller

    95.9k12195260




    95.9k12195260













    • 'infinite recursion' would be the better term - and at least in theory, it offers another interesting outcome: crash due to stack overflow (if TCO is not applied)...

      – Aconcagua
      Jan 14 at 7:51











    • @Aconcagua - You say to-may-to, I say to-mah-to. Both are Turing equivalent after all ;). Just sticking to the OP's way of phrasing and thinking about it.

      – StoryTeller
      Jan 14 at 7:54






    • 2





      And don't forget the option of a deadlock should VS be using a non recursive synchronization primitive.

      – StoryTeller
      Jan 14 at 7:55



















    • 'infinite recursion' would be the better term - and at least in theory, it offers another interesting outcome: crash due to stack overflow (if TCO is not applied)...

      – Aconcagua
      Jan 14 at 7:51











    • @Aconcagua - You say to-may-to, I say to-mah-to. Both are Turing equivalent after all ;). Just sticking to the OP's way of phrasing and thinking about it.

      – StoryTeller
      Jan 14 at 7:54






    • 2





      And don't forget the option of a deadlock should VS be using a non recursive synchronization primitive.

      – StoryTeller
      Jan 14 at 7:55

















    'infinite recursion' would be the better term - and at least in theory, it offers another interesting outcome: crash due to stack overflow (if TCO is not applied)...

    – Aconcagua
    Jan 14 at 7:51





    'infinite recursion' would be the better term - and at least in theory, it offers another interesting outcome: crash due to stack overflow (if TCO is not applied)...

    – Aconcagua
    Jan 14 at 7:51













    @Aconcagua - You say to-may-to, I say to-mah-to. Both are Turing equivalent after all ;). Just sticking to the OP's way of phrasing and thinking about it.

    – StoryTeller
    Jan 14 at 7:54





    @Aconcagua - You say to-may-to, I say to-mah-to. Both are Turing equivalent after all ;). Just sticking to the OP's way of phrasing and thinking about it.

    – StoryTeller
    Jan 14 at 7:54




    2




    2





    And don't forget the option of a deadlock should VS be using a non recursive synchronization primitive.

    – StoryTeller
    Jan 14 at 7:55





    And don't forget the option of a deadlock should VS be using a non recursive synchronization primitive.

    – StoryTeller
    Jan 14 at 7:55













    4














    The behaviour is undefined. The reason it "worked" in Visual Studio 2013 is that it didn't implement thread safe initialisation of function statics. What is probably happening is that the first call to GetA() creates a and calls the constructor. The second call to GetA() then just returns the partially constructed a. As the body of your constructor doesn't initialise anything calling Print() doesn't crash.



    Visual Studio 2017 does implement thread safe initialisation. and presumably locks some mutex on entry to GetA() if a is not initialised, the second call to GetA() then encounters the locked mutex and deadlocks.



    Note in both cases this is just my guess from the observed behaviour, the actual behaviour is undefined, for example GetA() may end up creating 2 instances of A.






    share|improve this answer




























      4














      The behaviour is undefined. The reason it "worked" in Visual Studio 2013 is that it didn't implement thread safe initialisation of function statics. What is probably happening is that the first call to GetA() creates a and calls the constructor. The second call to GetA() then just returns the partially constructed a. As the body of your constructor doesn't initialise anything calling Print() doesn't crash.



      Visual Studio 2017 does implement thread safe initialisation. and presumably locks some mutex on entry to GetA() if a is not initialised, the second call to GetA() then encounters the locked mutex and deadlocks.



      Note in both cases this is just my guess from the observed behaviour, the actual behaviour is undefined, for example GetA() may end up creating 2 instances of A.






      share|improve this answer


























        4












        4








        4







        The behaviour is undefined. The reason it "worked" in Visual Studio 2013 is that it didn't implement thread safe initialisation of function statics. What is probably happening is that the first call to GetA() creates a and calls the constructor. The second call to GetA() then just returns the partially constructed a. As the body of your constructor doesn't initialise anything calling Print() doesn't crash.



        Visual Studio 2017 does implement thread safe initialisation. and presumably locks some mutex on entry to GetA() if a is not initialised, the second call to GetA() then encounters the locked mutex and deadlocks.



        Note in both cases this is just my guess from the observed behaviour, the actual behaviour is undefined, for example GetA() may end up creating 2 instances of A.






        share|improve this answer













        The behaviour is undefined. The reason it "worked" in Visual Studio 2013 is that it didn't implement thread safe initialisation of function statics. What is probably happening is that the first call to GetA() creates a and calls the constructor. The second call to GetA() then just returns the partially constructed a. As the body of your constructor doesn't initialise anything calling Print() doesn't crash.



        Visual Studio 2017 does implement thread safe initialisation. and presumably locks some mutex on entry to GetA() if a is not initialised, the second call to GetA() then encounters the locked mutex and deadlocks.



        Note in both cases this is just my guess from the observed behaviour, the actual behaviour is undefined, for example GetA() may end up creating 2 instances of A.







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Jan 14 at 8:16









        Alan BirtlesAlan Birtles

        8,6651933




        8,6651933






























            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.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54177087%2fwhat-happens-when-a-constructor-function-calls-itself-in-vs2013%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

            Biblatex bibliography style without URLs when DOI exists (in Overleaf with Zotero bibliography)

            ComboBox Display Member on multiple fields

            Is it possible to collect Nectar points via Trainline?