Unobvious feature in variable definition syntax
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.
Question: what type of variable b? Not at all one that could be assumed at first glance.
Of course, the type of the variable b is not B, otherwise this article would not have been :) I will not immediately give the answer, but instead I will tell you how to get to it without digging into the thousand-page standard.
First, add some debugging printing:
If you try to run this code, it turns out that nothing is output at all . But if you replace line (1) with
suddenly everything starts to work.
Now let's look carefully at the compiler output with warnings turned on as much as possible.
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 did i turn out to be unused if it is used on the next line?
In principle, this information is enough to give a little thought to answer the question. But if smart thoughts do not occur, and you want to play a little more, why not just ask the compiler? RTTI comes to the rescue.
When compiling GCC 4.3, the output of this program is a string
in which the information about the type of the variable we need is encrypted (of course, another compiler will give a different line, the output format type_info :: name () is not described in the standard and is left to the discretion of the developer). C ++ filt will help us find out what these letters and numbers mean.
Here is the answer: this is a function that takes an input of type A parameter and returns a value of type B.
It remains to understand why our line was interpreted in such an unexpected way. The thing is that in a variable type declaration, extra brackets around the name are ignored. For example, we can write
and that will mean exactly the same thing as
Therefore, the long-suffering line (1) can be rewritten without changing the meaning, removing an extra pair of brackets:
Now we can see with naked eyes that b is a function declaration with one argument of type A, which returns a value of type B.
At the same time, we explained the strange thing about the unused variable i - indeed, it has nothing to do with the formal parameter i.
We only need to explain to the compiler what we really want from it - that is, to get a variable of type B, initialized by a variable of type A. The easiest way is to add extra brackets, like this:
or so:
This is enough to convince the parser that this is not a function declaration.
Alternatively, you can use the constructor invocation form using assignment, unless the constructor is explicitly declared:
Despite the presence of the '=' sign, no unnecessary copying occurs here, which can be easily verified by having a private copy constructor in class B.
Or you can simply enter an additional variable:
True, this will require unnecessary copying of the variable a, but in many cases this is acceptable.
Choose the method that seems more understandable to you :)
Inspired by a post on StackOverflow
PS Thank you sse for clarification.
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 type of variable b? Not at all one that could be assumed at first glance.
Analysis
Of course, the type of the variable b is not B, otherwise this article would not have been :) I will not immediately give the answer, but instead I will tell you how to get to it without digging into the thousand-page standard.
First, add some debugging printing:
#include
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 output at all . But if you replace line (1) with
B b(A(1));
suddenly everything starts to work.
Now let's look carefully at the compiler output with warnings turned on as much as possible.
$ 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 did i turn out to be unused if it is used on the next line?
In principle, this information is enough to give a little thought to answer the question. But if smart thoughts do not occur, and you want to play a little more, why not just ask the compiler? RTTI comes to the rescue.
#include
#include
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 output of this program is a string
F1B1AE
in which the information about the type of the variable we need is encrypted (of course, another compiler will give a different line, the output format type_info :: name () is not described in the standard and is left to the discretion of the developer). C ++ filt will help us find out what these letters and numbers mean.
$ c++filt -t F1B1AE
B ()(A)
Here is the answer: this is a function that takes an input of type A parameter and returns a value of type B.
Cause
It remains to understand why our line was interpreted in such an unexpected way. The thing is that in a variable type declaration, extra brackets around the name are ignored. For example, we can write
int (v);
and that will mean exactly the same thing as
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 we can see with naked eyes that b is a function declaration with one argument of type A, which returns a value of type B.
At the same time, we explained the strange thing about the unused variable i - indeed, it has nothing to do with the formal parameter i.
Workarounds
We only need to explain to the compiler what we really want from it - that is, to get a variable of type B, initialized by 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 constructor invocation form using assignment, unless the constructor is explicitly declared:
B b = A(i);
Despite the presence of the '=' sign, no unnecessary copying occurs here, which can be easily verified by having 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 unnecessary copying of the variable a, but in many cases this is acceptable.
Choose the method that seems more understandable to you :)
Inspired by a post on StackOverflow
PS Thank you sse for clarification.