Why Class C++ can be equal to a variable - c++

I have this code:
#include <iostream>
using namespace std;
class complex
{
double re;
double im;
public:
complex(): re(0), im(0) {}
complex(double x) {re = x, im = x;}
complex(double x, double y) {re=x, im =y;}
void print() {cout << re << " " << im;}
};
int main()
{
complex c1;
double i=2;
c1 = i;
c1.print();
return 0;
}
My question is, why the code in this line compiles.
c1 = i;
The compiler gives no error(or warning), why?

Let's examine what happens in the line.
c1 = i;
What happens is that operator= is called for class complex, which, in this case, is implicitly defined.
/////////////////////////////////////
//implicit copy assignment operator//
/////////////////////////////////////
complex& operator=(const complex& cmp)
{
//the default implicit function of this operator is copying every member of cmp into this.
}
So it takes const complex& as argument, which can be bound to rvalue of type complex, then the compiler searches to see if there is a constructor accepting double parameter, so the expression is resolved into.
c1 = complex(i);
Which, obviously, can be executed.

c1 = i invokes the constructor complex(double x) {re = x, im = x;}. If you'd prefer it didn't you can specify the constructor as explicit, like so:
explicit complex(double x) {re = x, im = x;}
Then the compiler would issue an error # c1 = i;

Because your constructor isn't explicit.
A constructor with a single non-default parameter (until C++11) that is declared without the function specifier explicit is called a converting constructor.
So having a constructor with a single non-default parameter, such as your complex(double x) will enable you to write complex s = 5.0 and call said constructor.

I have added some comments that explain how this program works. When you write c1 = i; then a temporary of type complex will be created using the converting constructor and then that temporary will be assigned to c1 using the assignment operator.
#include <iostream>
using namespace std;
class complex
{
double re;
double im;
public:
complex(): re(0), im(0){}
//this converting constructor will be used to create a temporary of type complex from variable i
complex(double x)
{
std::cout<<"single parameter constructor used"<<std::endl;
re = x;
im = x;
}
complex(double x, double y) {re=x, im =y;}
void print() {cout << re << " " << im;}
//this assignment operator = will be used to assign the temporary(of type complex) created above to variable c1
complex& operator=(const complex &rhs)
{
std::cout<<"assignment operator used"<<std::endl;
}
complex(const complex&)
{
std::cout<<"copy constructor used"<<std::endl;
}
};
int main()
{
complex c1;
double i=2;
c1 = i;//first a temporary of type complex will be created using the converting constructor and then that temporary will be assigned to c1 using the assignment operator=
c1.print();
return 0;
}
If you want to prevent this kind of usage, you can make the converting constructor explicit by adding the keyword explicit in front of the converting constructor.

Related

Working of parameterized constructor in C++ when variables are initailized vs uninitialized

Below C++ code works.
#include<iostream>
using namespace std;
class Complex{
private:
int real, imag;
public:
Complex(int r=0, int i=0){
real = r; imag = i;
}
};
int main()
{
Complex c1(10, 5), c2(2, 4);
Complex c3;
}
When the parameterized constructor's variables r and i are uninitialized(eg: Complex(int r, int i)), the compiler throws up the error
main.cpp:19:13: error: no matching function for call to ‘Complex::Complex()’
19 | Complex c3;
| ^~
main.cpp:10:5: note: candidate: ‘Complex::Complex(int, int)’
10 | Complex(int r, int i){
| ^~~~~~~.
I understood this to be an issue with the statement Complex c3;. Pardon me for being naive, but it's unclear why it works this way in the initial code snippet itself. Hope someone can clarify this.
The Complex constructor you show, with default arguments, can be called with two, one or zero arguments. If no arguments is used then the default values will be used.
But if you remove the default values, you no longer have a default constructor, a constructor that can be used without arguments.
It's really exactly the same as normal functions with default argument values...
Lets say you have this function:
void foo(int arg = 0)
{
// Implementation is irrelevant
}
Now this can be called as:
foo(123); // Pass an explicit argument
Or as:
foo(); // Equivalent to foo(0)
If you remove the default value:
void foo(int arg)
{
// Implementation is irrelevant
}
Then calling the function without any argument is wrong:
foo(); // Error: Argument expected
The definition Complex(int r=0, int i=0) allows for 0, 1 or 2 parameters. Complex c3; is constructing a Complex with 0 parameters.
You could instead have overloads, e.g.
class Complex{
private:
int real, imag;
public:
Complex() : real(0), imag(0) {}
Complex(int r, int i) : real(r), imag(i) {}
};
Note that it is better to use the member initialisers than assignment statements in the body of the constructor.

How to access a class constructor parameter from the another class parameter

STRUGGLING WITH C++ CONSTRUCTOR ARGUMENTS
So, I've just came from TS/JS/Py and trying to understand C++ concepts. But I'm struggling with using the parameter of constructor of the class FOR declaring default value for an argument. Here is the code I'm trying to run:
double Phythagorean_Hypotenuse (int& a, int& b ) {
return sqrt((a * a) + (b * b));
};
class Triangle {
public:
int a;
int b;
double c;
Triangle(int a_param, int b_param, double c_param = Phythagorean_Hypotenuse(a_param, b_param)) {
a = a_param;
b = b_param;
c = c_param;
}
};
and inside of the main function
Triangle mytri_1(10, 20);
std::cout << mytri_1.a << std:endl;
But when I try to run this code, IDE is throwing me some errors like
[Error] 'a_param' was not declared in this scope
or
[Error] call to 'Triangle::Triangle(int, int, double)' uses the default argument for parameter 3, which is not yet defined
So, please, can someone who can fix this answer the question?
Thanks.
There are some issues that prevent your code from compiling, namely:
Constructors do not have return type.
double c_param = Phythagorean_Hypotenuse(a_param, b_param) is not valid for a parameter, a_param, b_param will not be recognized.
Recommend change:
Since the result of a hypothenuse calculation will most likely be a decimal value, c should be a double.
You can do something like this:
Running sample
#include <iostream>
#include <cmath>
double Phythagorean_Hypotenuse (int& a, int& b ) {
return sqrt((a * a) + (b * b));
};
class Triangle {
public:
int a;
int b;
double c; //should be double
//initializer list is a good practice for member initialization
Triangle(int a_param, int b_param)
: a(a_param), b(b_param), c(Phythagorean_Hypotenuse(a, b)) {}
};
int main(){
Triangle mytri_1(10, 20);
std::cout << mytri_1.a << std::endl;
std::cout << mytri_1.b << std::endl;
std::cout << mytri_1.c << std::endl;
}
Output:
10
20
22.3607
As the compiler is pointing out, the other constructor arguments are not available as default parameters for the c_param argument. Rather than using default values, just overload the constructor, including one that just accepts 2 parameters. This constructor can then invoke the other constructor that accepts all 3:
// Constructor overload that accepts all 3 parameters
Triangle(int a_param, int b_param, double c_param):
a(a_param), b(b_param), c(c_param) {
}
// Constructor overload that accepts just a and b, call the other constructor
// to set all 3 members
Triangle(int a_param, int b_param):
Triangle(a_param, b_param, Phythagorean_Hypotenuse(a_param, b_param)) {
}
Default parameter values cannot reference other parameters. You can define two overloads, one of which delegates to the other, to do what you want:
class Triangle {
public:
double a;
double b;
double c;
Triangle(double a_param, double b_param, double c_param)
: a{a_param},
b{b_param},
c{c_param}
{}
Triangle(double a_param, double b_param)
: Triangle{a_param, b_param, Phythagorean_Hypotenuse(a_param, b_param)}
{}
};
Live Demo
A few other notes:
Class constructors do not have a return type. I changed void Triangle(...) to Triangle(...)
I used constructor initialization lists instead of assignment in the constructor's body. There's likely no difference for small primitive values like ints or doubles, but it's a good habit to get into and can make a big difference for more complex types
int doesn't make sense for the type of c (or a or b for that matter). The sides of a triangle are unlikely to all be integers
There's no reason to pass parameters to Pythagorean_Hypotenuse by reference. It's simpler and likely faster to pass them by value

Main function of complex number class C++

I am working on an assignment for C++, I am very new to this language. The assignment is about implementing Newton Raphson Method for a function with complex roots. I have implemented the code.
I want to test my code now and I am having difficulty in getting my main function to work properly, there are some concepts I am not understanding that lead to my wrong implementation. I would appreciate some explanations so I can understand better. Thanks.
This is the example of my code:
Complex.h
#include<iostream>
#include<cmath>
using namespace std;
class Complex {
private:
double r;
double i;
public:
Complex(double real, double imaginary);
friend Complex operator+(const Complex& c1, const Complex& c2);
friend ostream& operator<<(ostream& outs, const Complex& number);
};
Complex.cpp
#include "testComplex.h"
Complex::Complex(double real = 0.0, double imaginary = 0.0) : r(real), i(imaginary) {}
Complex operator+(const Complex& c1, const Complex& c2) {
Complex result;
result.r = c1.r + c2.r;
result.i = c1.i + c2.i;
return result;
}
main.cpp
#include <iostream>
#include "testComplex.h"
using namespace std;
int main () {
Complex x;
Complex y;
Complex sum;
x = Complex(2, 4);
y = Complex(3, 0);
sum = x + y;
cout << "The sum (x + y) is: " << sum << endl;
return 0;
}
This is a part of the error that I am receiving:
testComplexmain.cc: In function ‘int main()’:
testComplexmain.cc:8:10: error: no matching function for call to ‘Complex::Complex()’
testComplexmain.cc:8:10: note: candidates are:
testComplex.h:15:2: note: Complex::Complex(double, double)
testComplex.h:15:2: note: candidate expects 2 arguments, 0 provided
testComplex.h:8:7: note: Complex::Complex(const Complex&)
testComplex.h:8:7: note: candidate expects 1 argument, 0 provided
testComplexmain.cc:9:10: error: no matching function for call to ‘Complex::Complex()’
You don't have a default constructor. The two-argument constructor could be used as one, since both arguments are optional; but you'll have to put the default arguments on the declaration in the header, not the definition in the source file, for them to be usable from main.
Personally, I'd use std::complex rather than reinventing it.
You don't have a default constructor because you put the default parameters in the definition, so you can only construct Complex instances by passing two parameters.
You could add a default constructor,
Complex() : r(0), i(0) {}
or put the default parameters in the function declaration instead of in the definition
Complex(double real = 0.0, double imaginary = 0.0);
or write your code like this:
int main () {
Complex x(2, 4);
Complex y(3, 0);
Complex sum = x + y;
cout << "The sum (x + y) is: " << sum << endl;
return 0;
}

built-in data type conversion to user defined data type c++

My question is regarding data type conversion in c++. Does c++ provides a implicit conversion for built-in data types ( int, float) to user defined data types?
In the following example, I am trying to add a double ( t4 = t3 + 1.0) with test object type and its working fine using + operator so is the double implicitly converted to test type object?
class test {
double d;
int m;
public:
test() {
d=0;
m=0;
}
test(double n) {
d=n;
}
const test operator+(const test& t) {
test temp;
temp.d = d+ t.d;
return temp;
}
};
int main() {
test t1(1.2);
test t2(2.5);
test t3, t4;
t3= t1+ t2;
t4 = t3 + 1.0;
return 0;
}
The constructor test(double n) declares an implicit conversion from double to test. In general, any constructor that is callable with exactly one argument (that includes constructors that can take more arguments, but have default-values for those), can be used as an implicit conversion, unless it is marked explicit:
struct Foo {
Foo(int x); // implicit conversion from int to Foo
explicit Foo(char c); // marked explicit - no implicit conversion
Foo(std::string a, double pi = 3.14159); // can be used as implicit
// conversion due to default
// argument
};
Edit: t4 = 1.0 + t3 does not work, because you have overloaded your operator+ as a member-function, and thus it is only considered if the first operand is of the type test - implicit conversion are not tried in this case. To make this work, make your operator a free function:
test operator+(const test& lhs, const test& rhs);
Yes, as long as your constructor test(double) is not explicit
// implicite convertible
test(double n)
{
d=n;
}
// not implicite convertible
explicit test(double n)
{
d=n;
}

Assignment vs Initialization in C++

I thought that constructors control initialization and operator= functions control assignment in C++. So why does this code work?
#include <iostream>
#include <cmath>
using namespace std;
class Deg {
public:
Deg() {}
Deg(int a) : d(a) {}
void operator()(double a)
{
cout << pow(a,d) << endl;
}
private:
int d;
};
int
main(int argc, char **argv)
{
Deg d = 2;
d(5);
d = 3; /* this shouldn't work, Deg doesn't have an operator= that takes an int */
d(5);
return 0;
}
On the third line of the main function, I am assigning an int to an object of class Deg. Since I don't have an operator=(int) function, I thought that this would certainly fail...but instead it calls the Deg(int a) constructor. So do constructors control assignment as well?
This is what's called implicit type conversion. The compiler will look to see if there's a constructor to directly change from the type you're assigning to the type you're trying to assign, and call it. You can stop it from happening by adding the explicit keyword in front of the constructor you wouldn't like to be implicitly called, like this:
explicit Deg(int a) : d(a) {}
Just to clarify JonM's answer:
For the line d = 3, an assignment operator is involved. 3 is being implicitly converted to a Deg, as JonM said, and then that Deg is assigned to d using the compiler-generated assignment operator (which by default does a member-wise assignment). If you want to prevent assignment, you must declare a private assignment operator (and do not implement it):
//...
private:
Deg& operator=(const Deg&);
}