I have reduced my real program to a toy example.
Say I have the following code:
struct A
{
A( string i ) { }
A( string *i ) { }
};
int main()
{
string s;
A("HELLO"); // Works
A(&s); // XCode: Declaration of reference variable 's' requires an initializer
// Visual Studio: error C2040: 's': 'A &' differs in levels of indirection from 'std::string'
}
I don't understand the error message. What does XCode mean "Declaration of reference variable 's' requires an initializer"? Why does A("HELLO"); work while A(&s) does not?
This is a phenomenon called most-vexing parse.
C++ treats A(&s); as a declaration of a variabled called s, whose type is A&. This won't work for several reasons:
References need to be initialized, and you are not initializing s in this example (note: this s is not the same s as the one from 2 lines before).
You already have a variable called s in the same scope, so this would be a redefinition.
To create an even smaller example, take a look at these two, equivalent lines of code that both declare a variable called x of type int:
int x;
int(x);
To fix the issue, you can simply use a proper declaration that uses the desired constructor, which doesn't suffer from the most-vexing parse problem:
A x1(&s); // this
auto x2 = A(&s); // or this
A{&s}; // or this, just to create a temporary
Related
This question already has answers here:
How to create temporary object in C++
(2 answers)
Why does C++ allow us to surround the variable name in parentheses when declaring a variable?
(2 answers)
C++ temporary class instantiation ambiguously
(2 answers)
Closed 3 years ago.
sorry for the question title but I didn't know a correct title for my problem. I have the following code example:
struct test {
test(int a) {
}
};
int main() {
test(1);
return 0;
}
The above example code works. Now i do (in my understanding) the same a bit differently:
struct test {
test(int a) {
}
};
int main() {
int a = 0;
test(a);
return 0;
}
When I compile this I get the following error:
error: redefinition of 'a' with a different type: 'test' vs 'int'
But in my opinion it gets really strange when I try this:
struct test {
test(int a) {
}
};
int main() {
int a = 0;
test((int)a);
return 0;
}
the above example works again and really confuses me since I don't see the difference (except casting an int to an int). Can anyone explain what's going on? Thank you in advance.
You forgot to give your test variable a name, causing test(a); to be a declaration of a variable named a of type test.
In the other cases, since test(1) and test((int)a) cannot be declarations, but must be some kind of call, your compiler will treat that as constructing a temporary object of type test without a name.
This is really tricky:
test(1);
This is a construction of test with argument 1.
int a = 1;
test(a);
The compiler reads this as test a; (instance of test named a – with default construction). That test doesn't even provide a default constructor isn't considered by compiler at this point.
The fix (found by OP):
int a = 1;
test((int)a);
Now, the compiler is explicitly told to read a as expression (but not as identifier).
The syntax of defining variables in C++ is kind of quirky...
When you do
test(1);
you create a temporary object of the test structure. This object will be destructed immediately.
But when you do
test(a);
you don't create a temporary test object, you actually define a variable named a. It's equivalent to
test a;
You can solve this problem by using curly-braces
test{a};
Or by using an explicit expression for the "argument" (like you do with your cast), as such can't be used as variable names. In similar situation where you need to disambiguate between variables and expressions it's common to use the unary + as in
test(+a);
In ISO C++ Standard (ISO/IEC 14882) 2003, at section 3.2:
In any translation unit, a template, type, function, or object can have no more than one definition.
Please note that The One Definition Rule (ODR) is an important concept in the C++ programming language.
If you want to have many functions with same name (and different signatures, of course), function overloading is a feature which you want.
In the following snippet, why does the line o.margin() = m; compile without fault? It easily deserves a warning, since it will almost invariably be a mistake. I would actually have thought it to be an error since it puts an R-Value on the left side of an assignment.
#include <iostream>
struct Margin
{
Margin(int val=0) : val(val) {};
int val;
};
struct Option
{
Margin m;
int z=0;
Margin margin()const { return m; }
int zoomLevel() { return z; }
};
int main()
{
Option o;
std::cout << "Margin is: "<< o.margin().val << std::endl;
Margin m = { 3 };
// The following line is a no-op, which generates no warning:
o.margin() = m;
// The following line is an error
// GCC 4.9.0: error: lvalue required as left operand of assignment
// clang 3.8: error: expression is not assignable
// MSVC 2015: error C2106: '=': left operand must be l-value
o.zoomLevel() = 2;
std::cout << "Margin is: "<< o.margin().val << std::endl;
return 0;
}
Output:
Margin is: 0
Margin is: 0
You are allowed to modify return types of class type (by calling non const methods on it):
3.10/5 from n4140
5 An lvalue for an object is necessary in order to modify the object
except that an rvalue of class type can also be used to modify its
referent under certain circumstances. [ Example: a member function
called for an object (9.3) can modify the object. —end example ]
your code:
o.margin() = m;
is actually the same as
o.margin().operator=( Margin(m) );
so non const method is called, if you change it to:
o.margin().val = m;
then you will get an error.
on the other hand here:
o.zoomLevel() = 2;
zoomLevel() returns non-class type, so you cannot modify it.
When o is an object of class type, operator= is a member function. The code o.margin() = m; is equivalent to o.margin().operator=(m);.
You are allowed to call members functions of temporary class objects, similar to how you access a member in o.margin().val.
Also, the class' assignment operator can be overridden and not be a no-op at all.
If you want to forbid such uses, since C++11 you can use a reference qualifier on the assignment operator:
Margin& operator=(const Margin&) & = default;
This will generate the following error on GCC 5.1:
error: passing 'Margin' as 'this' argument discards qualifiers [-fpermissive]
You might also want to check this related question.
Option::margin() is a const-accessible member function that returns a mutable Margin object.
Consequently, assigning the temporary is valid because using operator= on Margin is valid. In this case it has no side-effects and basically does nothing. A particular implementation of a C++ compiler may choose to implement semantic analysis and warn you, but it's completely outside the scope of the language.
Why does the first commented line compile correctly, whereas the second doesn't?
Why can a be given itself as a constructor argument, but b can't?
Aren't the two doing the same thing?
class Foo { Foo &operator =(Foo const &); /* Disable assignment */ };
int main()
{
Foo a = a; // OK
Foo b(b); // error C2065: 'b' : undeclared identifier
}
Update
Since it seems like it's compiler-dependent, it seems like the problem is more severe than I thought.
So I guess another part of the question is, is the following code valid or no?
It gives an error in GCC but Visual C++ executes it just fine.
int main()
{
int i = 0;
{ int *i(&i); }
return i;
}
In your first code, both declarations should compile. GCC is right there. Visual C++ Compiler has bug.
And in the second code, the inner declaration should not compile. GCC is right there too, and VC++ is wrong.
GCC is right in both cases.
A code like int a=a+100; and int a(a+100); is fine from syntax point of view. They might invoke undefined behavior depending on whether they're created in static storage duration or automatic storage duration.
int a = a + 100; //well-defined. a is initialized to 100
//a on RHS is statically initialized to 0
//then a on LHS is dynamically initialized to (0+100).
void f()
{
int b = b + 100; //undefined-behavior. b on RHS is uninitialized
int a = a + 50; //which `a` is on the RHS? previously declared one?
//No. `a` on RHS refers to the newly declared one.
//the part `int a` declares a variable, which hides
//any symbol with same name declared in outer scope,
//then `=a+50` is the initializer part.
//since a on RHS is uninitialized, it invokes UB
}
Please read the comments associated with each declaration above.
Note that variables with static storage duration is statically initialized to zero at compile time, and if they've initializer, then they're dynamically initialized also at runtime. But variables of POD types with automatic storage duration are not statically initialized.
For more detail explanation on static initialization vs dynamic initialization, see this:
What is dynamic initialization of object in c++?
In your first example, as you note, the behavior is undefined even though the syntax is okay. A compiler is therefore permitted to refuse the code (the undefined behavior must be guaranteed however; it is here, but it wouldn't be if the invalid initializations were never actually executed).
Your second example has a type error: A declaration is visible as soon as its declarator is seen, and in particular it is visible in its own initializer. MSVC++ delays the visibility: That's a known non-conformance issue in that compiler. E.g., with the EDG compiler (which has a Microsoft mode):
$ ./cfe --strict x.c
"x.c", line 4: error: a value of type "int **" cannot be used to initialize an
entity of type "int *"
{ int *i(&i); }
^
1 error detected in the compilation of "x.c".
$ ./cfe --microsoft x.c
"x.c", line 4: warning: variable "i" was declared but never referenced
{ int *i(&i); }
^
Considering the cost, are these cases the same?
// case 1
int a = 5;
// case 2
int a (5);
// case 3
int a;
a = 5
The three syntaxes are different, bear with me while I use a user defined type instead of int, I will go back to int later.
T a(5); // Direct initialization
T b = 5; // Implicit conversion (5->tmp) + copy-initialization
T c; c = 5; // Default initialization + assignment
In the first case the object a is constructed by means of a constructor that takes an int or a type that can be implicitly converted from int.
struct T {
T( int ); // T a(5) will call this directly
};
In the second case, a temporary object of type T is created by an implicit conversion from int, and then that temporary is used to copy construct b. The compiler is allowed to optimize the code away and perform just the implicit conversion in place of the final object (instead of using it to create the temporary. But all restrictions have to be verified:
class T {
T( T const & );
public:
explicit implicit T( int );
};
int main() {
T b = 5; // Error 1: No implicit conversion from int to T.
// Fix: remove the `explicit` from the constructor
// Error 2: Copy constructor is not accessible
}
The third case is default construction followed by assignment. The requirements on the type are that it can be default constructed (there is a constructor with no arguments, or there is no user defined constructor at all and the compiler will implicitly define it). The type must be assignable from int or there must be an implicit conversion from int to a type U that can be assigned to T. As you see, the requirements for the type in the tree cases differ.
Besides the semantics of the different operations, there is other important difference, not all of them can be used in all of the contexts. In particular, in an initialization list in a class you cannot use the implicit convert + copy initialize version, and you can only have the first half of default construct + assign.
// OK // error // ok but different
struct test { struct test { struct test {
T x; T x; T x;
test(int v) : x(v) {} test(int v) : x=5 {} test( int v ) {
x = v;
}
In the first case the attribute x is directly initialized with the value v. The second case is a syntax error. The third case first default initializes and then assigns inside the body of the constructor.
Going back to the int example, all of the requirements are met by the type, so there is almost no difference on the code that the compiler generates for the three cases, but still you cannot use the int b = 5; version inside an initializer list to initialize an integer attribute. Moreover, if a class has a member attribute that is a constant integer, then you cannot use the equivalent of int c; c =5; (third column above) as the member attribute becomes const when it enters the constructor block, that is, x = v; above would be trying to modify a constant and the compiler will complain.
As to the cost that each one has, if they can be used at all, they incur the same cost for an int (for any POD type) but not so for user defined types that have a default constructor, in which case T c; c = 5; will incur the cost of default construction followed by the cost of assignment. In the other two cases, the standard explicitly states that the compiler is allowed to generate the exact same code (once the constraints are checked).
First and second are exactly same as both are initialization. Third one is different, as this is assignment. These differences are as per the C++ Standard. However, the compiler can treat all three as same!
For the first two, there will be no difference.
From Standard docs, 8.5.11,
The form of initialization (using parentheses or =) is generally insignificant, but does matter when the initializer or the
entity being initialized has a class type; see below. A parenthesized initializer can be a list of expressions only when the
entity being initialized has a class type.
The third one is not an initialization but an assignment.
And considering the cost,
In the first two cases, you are creating an integer with a value 5.
In the third case, you are creating an integer with an undefined value and replace it with 5..
If you use an optimizing compiler, they will all compile to the same code. So they all have the same cost.
Yes, they all evaluate to the exact same assembler representation. You can test this e.g. with GCC by writing a dummy function and then producing the assembler output: g++ -S file.cxx -o file.s
Can anyone explain why the following code does not compile (on g++ (GCC) 3.2.3 20030502 (Red Hat Linux 3.2.3-49))?
struct X {
public:
enum State { A, B, C };
X(State s) {}
};
int main()
{
X(X::A);
}
The message I get is:
jjj.cpp: In function 'int main()':
jjj.cpp:10: 'X X::A' is not a static member of 'struct X'
jjj.cpp:10: no matching function for call to 'X::X()'
jjj.cpp:1: candidates are: X::X(const X&)
jjj.cpp:5: X::X(X::State)`
Is this bad code or a compiler bug?
Problem solved by Neil+Konrad. See the comments to Neil's answer below.
You've forgot the variable name in your definition:
int main()
{
X my_x(X::A);
}
Your code confuses the compiler because syntactically it can't distinguish this from a function declaration (returning X and passing X::A as an argument). When in doubt, the C++ compiler always disambiguates in favour of a declaration.
The solution is to introduce redundant parentheses around the X since the compiler forbids parentheses around types (as opposed to constructo calls etc.):
(X(X::A));
X(X::A);
is being seen a s a function declaration. If you really want this code, use:
(X)(X::A);
Just to make it crystal clear what happens. Look at this example
int main() {
float a = 0;
{
int(a); // no-op?
a = 1;
}
cout << a;
}
What will it output? Well, it will output 0. The int(a) of above can be parsed in two different ways:
Cast to int and discard the result
Declare a variable called a. But ignore the parentheses around the identifier.
The compiler, when such a situation appears where a function-style cast is used in a statement and it looks like a declaration too, will always take it as a declaration. When it can't syntactically be a declaration (the compiler will look at the whole line to determine that), it will be taken to be an expression. Thus we are assigning to the inner a above, leaving the outer a at zero.
Now, your case is exactly that. You are trying (accidentally) to declare an identifier called A within a class called X:
X (X::A); // parsed as X X::A;
The compiler then goes on to moan about a not declared default constructor, because the static, as it assumes it to be, is default constructed. But even if you had a default constructor for X, it of course is still wrong because neither A is a static member of X, nor a static of X can be defined/declared at block scope.
You can make it not look like a declaration by doing several things. First, you can paren the whole expression, which makes it not look like a declaration anymore. Or just paren the type that is cast to. Both of these disambiguations have been mentioned in other answers:
(X(X::A)); (X)(X::A)
There is a similar, but distinct ambiguity when you try to actually declare an object. Look at this example:
int main() {
float a = 0;
int b(int(a)); // object or function?
}
Because int(a) can be both the declaration of a parameter called a and the explicit conversion (cast) of the float-variable to an int, the compiler decides again that that is a declaration. Thus, we happen to declare a function called b, which takes an integer argument and returns an integer. There are several possibilities how to disambiguate that, based on the disambiguation of above:
int b((int(a))); int b((int)a);
You should declare an object as
X x(X::A);
Bug in your code.
Either of these two lines work for me:
X obj(X::A);
X obj2 = X(X::A);
As Neil Butterworth points out, X(X::A) is being treated as a function declaration. If you really want an anonymous object, (X)(X::A) will construct an X object and immediately delete it.
You could, of course, just do something like this:
int main()
{
// code
{
X temp(X::A);
}
// more code
}
This would be more readable and basically have the same effect.