61

I did a test here, but the output is a loop without ending, I don't know why.

Actually, I am doing another test, but when I wrote this, I don't understand how the loop occurred. It is output "ABC" repeatedly.

#include <map>
#include <string>
#include <iostream>

class test
{
public:
   std::map <int, int> _b;
   test();
   test (std::map<int, int> & im);
   ~test();
   };

test::test()
{
  std::cout<<"abc";
  _b.clear();
  _b[1]=1;
  test(_b);
}

test::test(std::map <int, int>& im)
{
   std::cout<<im[1];
}

test::~test() {};

int main ()
{
   test a;  
}
templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
CJAN.LEE
  • 1,108
  • 1
  • 11
  • 20

4 Answers4

94

The issue here is that the compiler interprets the statement

test(_b);

not as code that creates a temporary object of type test passing in parameter _b, but as a variable declaration for a variable named _b of type test, using the default constructor. Consequently, what looks like a piece of code that creates a temporary test object using the second constructor is instead recursively creating a new object of type test and invoking the constructor another time.

To fix this, you can give the variable an explicit name, such as

test t(_b);

This can only be interpreted as a variable of type test named t, initialized using the second constructor.

I have never seen this before, and I've been programming in C++ for years. Thanks for showing me yet another corner case of the language!

For an official explanation: According to the C++03 ISO spec, §6.8:

There is an ambiguity in the grammar involving expression-statements and declarations: An expression-statement with a function-style explicit type conversion (5.2.3) as its leftmost subexpression can be indistinguishable from a declaration where the first declarator starts with a (. In those cases the statement is a declaration.

(My emphasis). In other words, any time C++ could interpret a statement as either an expression (the temporary object cast) or as a declaration (of a variable), it will pick the declaration. The C++ spec explicitly gives

T(a);

As an example of a declaration, not a cast of a to something of type T.

This is C++'s Most Vexing Parse - what looks like an expression is instead getting interpreted as a declaration. I've seen the MVP before, but I have never seen it in this context.

starball
  • 20,030
  • 7
  • 43
  • 238
templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
  • 1
    Thanks, this is surprisingly hard to Google for. – Antimony Apr 24 '13 at 20:11
  • Good answer! Any specification references available to prove/disprove that the compiler is interpreting this correctly? FWIW, codepad.org exhibited the same behavior – Tom Apr 24 '13 at 20:11
  • Ah, I found an explanation. It's called "Most Vexing Parse". By the way, in C++11 you can fix this by changing it to a `test{_b}`. (Use braces instead of parenthesis) http://en.wikipedia.org/wiki/Most_vexing_parse – Antimony Apr 24 '13 at 20:16
  • After inspecting the generated code, I was heading towards this, but couldn't find chapter and verse. When I replaced the original `test(_b);` with `test(_boggle);` and it compiled OK and behaved as before, I had an 'aha!' – Roddy Apr 24 '13 at 20:22
  • I've actually seen this before. It was asked about in another thread on SO. – David G Apr 24 '13 at 20:24
  • @Yakk: Or... [one of many other forms](http://stackoverflow.com/a/11691101/500104). ;) – Xeo Apr 24 '13 at 20:42
0

the problem is from constructor you again calling the contructor test(_b)

test::test(){std::cout<<"abc";_b.clear();_b[1]=1;test(_b);}

here is what happens

everytime you call test(_b) it first calls default constructor test::test and it in turns calls the test(_b) and the loop goes on and on untill the stack overflows.

remove the test(_b) from the default constructor

Pradheep
  • 3,553
  • 1
  • 27
  • 35
0

I'm not familiar with the particularities of the standard, but it may be that calling a constructor within a constructor is undefined. As such it could be compiler dependent. In this particular case it causes infinite recursion of your default constructor without ever calling your constructor with the map argument.

C++ FAQ 10.3 has an example with a constructor that has two parameters. If you add an int parameters to your second constructor such as test(map, int), it exhibits a somewhat normal behaviour.

For good form I would simply change test::test(std::map <int, int>& im) for test::testInit(std::map <int, int>& im), and test(_b) to testInit(_b).

OlivierD
  • 362
  • 1
  • 10
  • It does call another constructor, but it's not the same constructor as before. I don't see why that would cause the infinite recursion that's exhibited here. – templatetypedef Apr 24 '13 at 20:02
0

I'm pretty sure that you are not actually "calling the constructor" since they are not directly callable IIRC. The legalese had to do with constructors not being named functions - I don't have a copy of the Standard handy or I might quote it. I believe what you are doing with test(_b) is creating an unnamed a temporary which invokes the default constructor again.

D.Shawley
  • 58,213
  • 10
  • 98
  • 113
  • It's not creating an unnamed temporary and calling the default constructor; instead, it's creating a variable named `_b` and invoking the default constructor on it. See my answer for more details. – templatetypedef Apr 24 '13 at 20:10
  • Thanks. I remembered what it was doing just not the exact reason. I had forgotten how twisted the C/C++ declaration rules actually are. – D.Shawley Apr 24 '13 at 20:11