A completely innocent-looking piece of C ++ code is proposed. There are no templates, no virtual functions, no inheritance, but the creators of this wonderful language hid a rake in the middle of a clean field.
struct A {
A ( int i) {}
};
struct B {
B (A a) {}
};
int main () {
int i = 1;
B b(A(i)); // (1)
return 0;
}
* This source code was highlighted with Source Code Highlighter .
Question: What is the type of variable b? Not the one that would be supposed at first glance.
')
Analysis
Of course, the type of variable b is not B, otherwise this article would not exist :) I will not give an answer right away, but instead I will tell you how you can reach it without digging into the thousand-page standard.
First, let's add some debugging output:
#include <iostream>
struct A {
A ( int i) { std::cout << 'A' ;}
};
struct B {
B (A a) { std::cout << 'B' ;}
};
int main () {
int i = 1;
B b(A(i)); // (1)
return 0;
}
* This source code was highlighted with Source Code Highlighter .
If you try to run this code, it turns out that
nothing is displayed
at all . But if you replace the line (1) with
B b(A(1));
suddenly everything starts working.
And now we will look attentively at the output of the compiler with the maximum warning turned on.
$ g++ -W -Wall test.cpp
x.cpp:2: warning: unused parameter 'i'
x.cpp:6: warning: unused parameter 'a'
x.cpp: In function 'int main()':
x.cpp:10: warning: unused variable 'i'
With the first two lines, everything is clear; indeed, the parameters of the constructors are not used. But the last line looks very strange. How was the variable i unused if it is used in the next line?
In principle, this information is enough to, a little thought, to answer the question. But if clever thoughts do not come to mind, and you want to add a little more, why not just ask the compiler? RTTI comes to the rescue.
#include <iostream>
#include <typeinfo>
struct A {
A ( int i) {}
};
struct B {
B (A a) {}
};
int main () {
int i = 1;
B b(A(i)); // (1)
std::cout << typeid(b).name() << std::endl;
return 0;
}
* This source code was highlighted with Source Code Highlighter .
When compiling GCC 4.3, the result of this program is the string
F1B1AE
in which the type of variable information we need is encrypted (of course, another compiler will
produce another string, the output format
type_info :: name () is not described in the standard and left to the discretion of the developer). C ++ filt will help us to find out what these letters and numbers mean.
$ c++filt -t F1B1AE
B ()(A)
Here is the answer: this is a function that takes a type A parameter as an input and returns a value of type B.
Cause
It remains to understand why our string was interpreted in such an unexpected way. The thing is that in the type declaration of a variable, the extra brackets around the name are ignored. For example, we can write
int (v);
and it will mean exactly the same thing
int v;
Therefore, the long-suffering line (1) can be rewritten without changing the meaning, removing an extra pair of brackets:
B b(A i);
Now it can be seen with the naked eye that b is a declaration of a function with one argument of type A, which returns a value of type B.
At the same time, we explained the weird worning about the unused variable i - indeed, it has nothing to do with the formal parameter i.
Workarounds
It only remains for us to explain to the compiler what we really want from it - that is, to get a variable of type B, initialized to a variable of type A. The easiest way is to add extra brackets, like this:
B b((A(i)));
or so:
B b((A)(i));
This is enough to convince the parser that this is not a function declaration.
Alternatively, you can use the form of invoking the constructor with an assignment, unless the constructor is explicit:
B b = A(i);
Despite the presence of the '=' sign, no extra copying happens here, as can be easily seen by installing a private copy constructor in class B.
Or you can simply enter an additional variable:
A a(i);
B b(a);
True, this will require extra copying of the variable a, but in many cases this is acceptable.
Choose the method that seems more understandable to you :)
Inspired by the post in StackOverflowPS Thank you
sse for clarifying.