expected primary-expression before ‘...’, c++ compile error - c++

There are quite a few posts on SO with similar titles, but they seem to be triggered by various syntactic errors and I didn't see a consistent pattern yet..
using namespace std;
class A
{
public:
A(int a_) : a(a_) {}
int a;
};
int main()
{
A x{3};
A y{0};
if ((y=x).a)
cout << y.a << endl;
int i = 1;
if (int j = i)
cout << j << endl;
if ((A z = x).a) // error: expected primary-expression before ‘z’
cout << z.a << endl;
(int m = 1); // error: expected primary-expression before ‘int’
}
Am I wrong to assume A z = x is an assignment expression, which should have the same value as z?

Am I wrong to assume A z = x is an assignment expression
Yes, you are wrong. There is no assignment going on here. The = in this statement represents initialization, not assignment. The statement A z = x; defines the variable z, where z is constructed from x. The copy constructor is used here, not copy assignment. It is a declaration statement, not an expression statement.
Your confusion is reasonably common, and it is made worse by the fact that the condition in an if statement can be a declaration of a single non-array variable with a brace-or-equals initializer. Syntactically, a declaration with an "equals" initializer can look a lot like an assignment. A big difference, as you discovered, is that you cannot treat the declaration as a sub-expression. The condition is either a declaration or an expression, not a mix of both.
The good news is that C++-17 added an optional init-statement to the if statement syntax. So what you appear to want would be achieved by the following.
if ( A z = x; z.a ) // Semicolon separates init-statement from condition
cout << z.a << endl;
// At the end of the `if` statement, `z` goes out of scope.

You can't declare a variable in an if statement in that fashion. The declaration has to be of the form:
if (X x = y) ... (or if (auto x = y) ...)
However, you can still achieve what you are trying to do if you provide a suitable conversion operator in class A, like this:
#include <iostream>
using namespace std;
class A
{
public:
A(int a_) : a(a_) {}
int a;
operator bool () { return a != 0; }
};
int main()
{
A x{3};
const A &y = x;
if (y.a)
cout << y.a << endl;
if (A z = x)
cout << z.a << endl;
}
Live demo

Related

unable to include multiple expressions in initialization of a variable c++

It is stated on the site cppreference.com, something like that
For each declarator, the initializer may be one of the following:
( expression-list ) (1)
= expression (2)
{ initializer-list } (3)
comma-separated list of arbitrary expressions and braced-init-lists in parentheses
But in my code
int main(){
int a,b=5,c(a,b);
return 0;
}
when I try to compile, the following error occurs
...error: expression list treated as compound expression in initializer [-fpermissive]
My question is, if list of multiple expressions is allowed in such style of initialization, then why the compiler is not accepting it with variable c?
What am I missing?
All right, let's look at this:
int main(){
int a,b=5,c(a,b);
return 0;
}
What do you expect c(a,b) to actually do?
Let's simplify this just slightly:
int main(){
int a,b=5;
int c(a,b);
return 0;
}
This will generate the same syntax error, but it now stands alone. So...
Your code would work if there were a constructor for int that took two ints as parameters. This would also compile:
int c(int a, int b);
But in that case, you're actually defining a function.
Also, this works:
int main() {
int a = 5;
int b = 10;
int c(b);
std::cout << "C == " << c << std::endl;
}
That works because an int can be initialized from a single int. But you're getting an error because you can't initialize an int from two other ints.
This works:
#include <iostream>
class MyClass {
public:
MyClass(int a, int b): value(a + b) {}
int value;
};
int main() {
int a = 5;
int b = 10;
MyClass c(a, b);
std::cout << "C == " << c.value << std::endl;
}
And maybe that's what the article you read was trying to tell you. Note: cpppreference is NOT a good site for learning C++. Get a good book.

difference between { } and equal sign variables

I'm somewhat new to c++ programming. I couldn't find my answer any where on google so hopefully it can be answered here.
is there a difference between the following
unsigned int counter{ 1 };
or
unsigned int counter = 1;
the book uses the first option and its confusing to me because it doesn't explain the difference. below is the following code from the book i'm following.
#include <iostream>
#include <iomanip>
#include <cstdlib> // contains function prototype for rand()
using namespace std;
int main()
{
for (unsigned int counter{ 1 }; counter <= 20; ++counter) {
cout << setw(10) << (1 + rand() % 6);
// if counter is divisible by 5, start a new line of output
if (counter % 5 == 0) {
cout << endl;
}
}
}
Yes, they are two different types of initialization in C++.
The first one, i.e., unsigned int counter{ 1 }; is a direct initialization.
Whereas, the second one, i.e., unsigned int counter = 1;, is a copy initialization.
For all the details you can directly refer to the documentation.
However, I can emphasize:
Copy-initialization is less permissive than direct-initialization: explicit constructors are not converting constructors and are not considered for copy-initialization.
Initialization references here
For unsigned int type (such in your case), there are no real differences between the two initializations.
Note
The usage of curly braces in the first statement (unsigned int counter{ 1 }) provides an additional constraint:
Otherwise (if T is not a class type), if the braced-init-list has only one element [...], T is direct-initialized [...], except that narrowing conversions are not allowed.
In other words, the usage of curly braces in the initialization does not allow data looseness.
That is:
unsigned int counter{ 12.3 }; // error!!!
won't compile because you are trying to initialize an integer with a floating-point value.
Note this is a "property" of curly braces in the initialization. It is not strictly related to the initialization type.
In fact, you can also write:
unsigned int counter = { 12.3 }; // error!
which is, instead, a copy initialization, but having curly braces does not allows narrowing conversions.
Adding to the other explanations above.
I would use the first option(unsigned int counter{ 1 };) to initialize a list of values for a variable and for a single value, I would prefer to use the second option(unsigned int counter = 1;)
Hope this helps.
Consider the following demonstrative program.
#include <iostream>
struct A
{
int x;
explicit A( int x = 0 ) : x( x ) {}
};
int main()
{
A a1 { 10 };
std::cout << "a1.x = " << a1.x << '\n';
// A a2 = { 10 };
}
In this declaration
A a1 { 10 };
there is used the direct initialization.
And in the commented declaration
// A a2 = { 10 };
that can be also rewritten like
// A a2 = 10;
there is used the copy-initialization. But the constructor declared with the specifier explicit. So the compiler will issue an error. That is it is unable to convert the integer object 10 to the type of A implicitly.
You could write instead
A a2 = A{ 10 };
That is calling the constructor explicitly.
Of course for fundamental types there is no difference because neither constructor is applied except that narrowing conversion is not allowed when the braced initialization is used as for example
int x { 1.0 };
There is a big difference when the type specifier is the placeholder auto.
For example
auto x = 10;
x has the type int.
auto x { 10 };
x again has the type int.
auto x = { 10 };
Now x has the type std::initializer_list<int>.
For example you could rewrite your loop
for (unsigned int counter{ 1 }; counter <= 20; ++counter) {
the following way
for ( auto counter{ 1u }; counter <= 20; ++counter) {
but you may not write
for ( auto counter = { 1u }; counter <= 20; ++counter) {
because in this case the type of the variable counter is std::initializer_list<unsigned int>.
So in general you have the following forms of initializations
T x = value;
T x = { value };
T x( value );
T x { value };
For example
#include <iostream>
int main()
{
int x1 = 1;
std::cout << "x1 = " << x1 << '\n';
int x2 = ( 2 );
std::cout << "x2 = " << x2 << '\n';
int x3( 3 );
std::cout << "x3 = " << x3 << '\n';
int x4{ 4 };
std::cout << "x4 = " << x4 << '\n';
}
The program output is
x1 = 1
x2 = 2
x3 = 3
x4 = 4
But there is one more situation when instead of T() you should use T{} as an initializer. It is when template functions are used.
Consider the following demonstrative program
#include <iostream>
template <class>
void f()
{
std::cout << "f<T>() is called\n";
}
template <int>
void f()
{
std::cout << "f<int>() is called\n";
}
int main()
{
f<int()>();
f<int{}>();
}
Its output is
f<T>() is called
f<int>() is called
The construction int() used as a template argument specifiers the type template argument int while the construction int{} used as a template argument specifiers a non-type template argument of the type int equal to 0.
unsigned int counter = 1 ;
This style of initialization is inherited from C language.
unsigned int counter {1} ;
This style of initialization is of C++.
The difference is if you provide a wrong value while using C++ style initialization
for example:
unsigned int counter {-1} ;
This will give error (use -std=c++11 to compile it)
But this will not give any error.
unsigned int counter = -1 ;

How to use two constructors?

I have a code like this.
#include <iostream>
#include <vector>
using namespace std;
class AbstractValueC{
private:
long age = 0;
int abs;
bool spec = false;
public:
AbstractValueC() {
abs = 0;
}
AbstractValueC(int& a, bool b = false){
abs = *a;
spec = b;
}
AbstractValueC(int a, bool b = false){
abs = a;
spec = b;
}
int get_value() const {
return abs;
}
long get_age(){
return age;
}
};
int main(){
vector<AbstractValueC> v;
int init = 1;
int next = 2;
v.push_back(AbstractValueC(std::move(init)));
cout << "I have created first v element." << endl;
v.push_back(AbstractValueC(std::move(next)));
cout << "I have created second v element." << endl;
std::vector<AbstractValueC> result;
for (auto a : v) {
int pre = a.get_value();
result.push_back(AbstractValueC(pre));
}
cout << "I have created result." << endl;
return 0;
}
Please tell me how to solve the error of
invalid type argument of unary ‘*’ (have ‘int’)
abs = *a;
^
and the error
call of overloaded ‘AbstractValueC(int&)’ is ambiguous
result.push_back(AbstractValueC(pre));
^
I actually have a custom type (Abs) in place of int for the abs member in my actual code. Also the requirement is that I cannot call the default constructor of Abs in main. I can use abs = Abs::null;. So please suggest a solution that avoid calling the default constructor for Abs if I replace int with Abs in the class definition.
invalid type argument of unary ‘*’ (have ‘int’)
AbstractValueC(int& a, bool b = false){
abs = *a;
spec = b;
}
Argument a is an int reference, not a pointer, so you can't dereference as *a.
The code should be abs = a;
call of overloaded ‘AbstractValueC(int&)’ is ambiguous
As #strom points out in the comments, you have 2 similar constructors:
AbstractValueC(int& a, bool b = false)
AbstractValueC(int a, bool b = false)
Which the compiler cannot readily distinguish between. You should get rid of one of them.
The real problem is that you are using two constructors with the same signature :
How does the compiler choose between using :
AbstractValueC v=AbstractValueC(value);//int constructor
And
AbstractValueC v=AbstractValueC(value);//reference constructor
When you have
Int value = 0;//for example
To get rid of this error(ambigueous constructor call) you could either change one of the constructors or replace your reference constructor with a pointer constructor
To change the constructor to a pointer, you need to create a value pointer in your class and create a contructor:
// in class declaration
Int *pointerValue = nullptr;
AbstractValueC(int* pointer, bool b = false);
// implementation
AbstractValueC::AbstractValueC(Int *pointer, bool b=false) {
pointerValue=pointer;
}

Unexpected output value with class constructor and class member variable

I need to understand why x(x + 1) happened only after I get out of the constructor.
class A
{
protected:
int x;
public:
A(int x = 5) : x(x + 1)
{
cout << "In A::A x=" << x << endl;
}
operator int() const { return x; }
};
void main()
{
A a1(10);
cout << a1 << endl ;
}
I was thinking I will get:
In A:: An x=11
11
But somehow I've got:
In A:: An x=10
11
You have two variables named x.
Inside the body of the constructor the argument variable will shadow the member variable. Whenever you use x inside the body of the constructor, it will be the argument, not the member.
To use the member variable you need to explicitly fetch it from the object, like this->x.
General tip: Don't use the same name for symbols in nested scopes. Besides solving this problem, it will also make the code easier to read and understand.
Your parameter is hiding the member variable of the same name - your definition is equivalent to
class A
{
protected:
int y;
public:
A(int x = 5) : y(x + 1)
{
cout << "In A::A x=" << x << endl;
}
operator int() const { return y; }
};
When a parameter has the same name as a member, you need to explicitly refer to the member with this->.
(The initialiser list follows its own rules.)

How to initialize the reference member variable of a class?

Consider the following code C++:
#include<iostream>
using namespace std;
class Test {
int &t;
public:
Test (int &x) { t = x; }
int getT() { return t; }
};
int main()
{
int x = 20;
Test t1(x);
cout << t1.getT() << " ";
x = 30;
cout << t1.getT() << endl;
return 0;
}
It is showing the following error while using gcc compiler
est.cpp: In constructor ‘Test::Test(int&)’:
est.cpp:8:5: error: uninitialized reference member ‘Test::t’ [-fpermissive]
Why doesn't the compiler directly call the Constructor?
That is because references can only be initialized in the initializer list. Use
Test (int &x) : t(x) {}
To explain: The reference can only be set once, the place where this happens is the initializer list. After that is done, you can not set the reference, but only assign values to the referenced instance. Your code means, you tried to assign something to a referenced instance but the reference was never initialized, hence it's not referencing any instance of int and you get the error.
My compiler emits this error:
error C2758: 'Test::t' : must be initialized in constructor base/member initializer list
And that's exactly what you must do. References must be initialized in the initializer list:
#include<iostream>
using namespace std;
class Test {
int &t;
public:
Test (int &x) : t(x) { } // <-- initializer list used, empty body now
int getT() { return t; }
};
int main()
{
int x = 20;
Test t1(x);
cout << t1.getT() << " ";
x = 30;
cout << t1.getT() << endl;
return 0;
}
Explanation:
If the reference is not in the initiliazer list, it's next to impossible for the compiler to detect if the reference is initialized. References must be initialized. Imagine this scenario:
Test (int &x, bool b)
{
if( b ) t = x;
}
Now it would be up to the caller of the constructor to decide if valid code was generated. That cannot be. The compiler must make sure the reference is initialized at compile time.