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;  
}
6

4 Answers 4

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.

Sign up to request clarification or add additional context in comments.

5 Comments

Good answer! Any specification references available to prove/disprove that the compiler is interpreting this correctly? FWIW, codepad.org exhibited the same behavior
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) en.wikipedia.org/wiki/Most_vexing_parse
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!'
I've actually seen this before. It was asked about in another thread on SO.
@Yakk: Or... one of many other forms. ;)
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

4 Comments

Why is this calling the default constructor? I figured this would call the implicit conversion constructor test::test(std::map<int, int>&), since it explicitly passes in _b.
Why does it call the default constructor? C++ doesn't chain constructors.
In C++ you should not call another constructor. This is different from Java for example.
@Oliver yes but that doesn't explain why the loop is occuring
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.

2 Comments

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.
Thanks. I remembered what it was doing just not the exact reason. I had forgotten how twisted the C/C++ declaration rules actually are.
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).

1 Comment

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.