I would like to ask about casting in C++.
I heard that when casting is ambiguous compiler should return an error,
but, just for better understanding, I tested it and it didn't, moreover, it used functions in quite weird order. When:
A foo;
B bar = foo;
it used casting operator, but when I typed:
bar = static_cast<B>(foo);
it used single argument constructor.
Can anyone explain why it acts in this way?
The whole code which I used:
#include <iostream>
#include <typeinfo>
using namespace std;
class B;
class A {
public:
A() {}
A (const B& x);
A& operator= (const B& x);
operator B();
};
class B {
public:
B() {}
B (const A& x) {
cout << "constructor B" << endl;
}
B& operator= (const A& x) {
cout << "Assign B" << endl;
return *this;
}
operator A() {
cout << "Outer B" << endl;
return A();
}
};
A::A (const B& x) {
cout << "constructor A" << endl;
}
A& A::operator= (const B& x) {
cout << "Assign A" << endl;
return *this;
}
A::operator B() {
cout << "Outer A" << endl;
return B();
}
int main ()
{
A foo;
// First one
B bar = foo;
bar = foo;
foo = bar;
// Second one
bar = static_cast<B>(foo);
B bar2 = static_cast<B>(foo);
foo = static_cast<A>(bar);
B bar3 = foo;
A foo2 = bar3;
A foo3 = B();
foo3 = B();
return 0;
}
Edit:
My output:
Outer A
Assign B
Assign A
Copy constructor B
Copy constructor B
Copy constructor A
Outer A
Outer B
Outer B
Assign A
The reason your compiler does not complain about ambiguity is that your constructors and assignment operators take a const A/B&, but operator A() and operator B() are not declared const. For the conversion of non-const objects, the compiler therefore prefers operator A/B().
I think that the rest can be explained with the rules of static_cast conversion, which in your code amounts to behavior as in direct initialization, and overload resolution (which is why the assignment operator is only called in the last example).
Related
The following code:
#include <iostream>
#include <string>
using namespace std;
void print(string a) { cout << a << endl; }
void print(string a, string b) { cout << a << b << endl; }
class A {
public:
string p;
A() { print("default constructor"); }
A(string a){ p = a; print("string constructor ", p); }
A(const A& o) { print("copy constructor"); }
A (A&& o) { print("move constructor"); }
A& operator=(const A& o) { print("copy assignment"); return *this; }
A& operator=(const A&& o) { cout << "move assignment to:" << p << " from:" << o.p << endl; return *this; }
~A() { print("destructor ", p); }
};
A operator+(const A& a, const A& b) {
cout << "add" <<endl;
A c("f");
return c;
}
A f(A& a, A& b, A& c) {
A d("e");
d = a+b+c;
print("after add");
return d;
}
int main() {
A a("a"); A b("b"); A c("c");
A whereDidThisGo {f(a,b,c)};
print("end");
}
has the following output:
string constructor a
string constructor b
string constructor c
string constructor e
add
string constructor f
add
string constructor f
move assignment to:e from:f
destructor f
destructor f
after add
end
destructor e
destructor c
destructor b
destructor a
Process exited after 0.06744 seconds with return value 0
Press any key to continue . . .
Where is the construction/destruction of the whereDidThisGo variable defined in main?
Where is the construction/destruction of the whereDidThisGo variable defined in main?
You do not see the ouptut for this due to named return value optimization(aka NRVO).
it's not a good optimization for people like me who are trying to learn constructors
You can disable this NRVO by providing the -fno-elide-constructors flag to the compiler. Demo.
Also, note that in your example the A::operator=(const A&&) should instead be:
//-----------vv------->no need for const here
A::operator=(A&&)
TIL about NRVO, it's not a good optimization for people like me who are trying to learn constructors haha.
Thank you for the answers, yes the move assignment should be a non-const pointer, I simply overlooked it.
At the last line myA = foo(myOtherB);, the function will return type an object of type A, thus; it will be like saying `myA = input, But why is the copy constructor is being?
output:
B foo()
A copy ctor //what calls this?
A op=
For a copy constructor to be called we will have to use the assignment operator during initialization such as: B newB = myOtherB;
#include <iostream>
using namespace std;
class A {
public:
A() { cout << "A ctor" << endl; }
A(const A& a) { cout << "A copy ctor" << endl; }
virtual ~A() { cout << "A dtor" << endl; }
virtual void foo() { cout << "A foo()" << endl; }
virtual A& operator=(const A& rhs) { cout << "A op=" << endl; }
};
class B : public A {
public:
B() { cout << "B ctor" << endl; }
virtual ~B() { cout << "B dtor" << endl; }
virtual void foo() { cout << "B foo()" << endl; }
protected:
A mInstanceOfA; // don't forget about me!
};
A foo(A& input) {
input.foo();
return input;
}
int main() {
B myB;
B myOtherB;
A myA;
myOtherB = myB;
myA = foo(myOtherB);
}
At the last line myA = foo(myOtherB);, the function will return type an object of type B
Not true. Your function returns an object of type A by value. That means, any value you feed this object to be constructed with will be used to construct a new object of that exact type. So in other words:
int foo(float a) {
return a + 0.5;
}
int u;
u = foo(9.3);
// u has a value of 10
Don't expect u to hold a value that a int cannot.
Same thing if you use user defined types:
A foo(A& input) {
input.foo();
return input; // this expression returns a new A
// using the value of `input`
}
A myA;
myA = foo(myOtherB);
// why would `myA` be anything else than the value of an A?
So then, what happen here?
B foo()
A copy ctor //what calls this?
A op=
A foo(A& input) {
input.foo(); // prints B foo, virtual call. a reference to A that
// points to an object of subclass type B
return input; // copy `input` into the return value object
}
Then, the operator= gets called.
See cppreference
Specifically:
The copy constructor is called whenever an object is initialized (by direct-initialization or copy-initialization) from another object of the same type (unless overload resolution selects a better match or the call is elided), which includes
initialization: T a = b; or T a(b);, where b is of type T;
function argument passing: f(a);, where a is of type T and f is void f(T t);
function return: return a; inside a function such as T f(), where a is of type T, which has no move constructor.
I want to inherit copy constructor of the base class using using keyword:
#include <iostream>
struct A
{
A() = default;
A(const A &) { std::cerr << __PRETTY_FUNCTION__ << std::endl; }
A( A &&) { std::cerr << __PRETTY_FUNCTION__ << std::endl; }
A& operator=(const A &) { std::cerr << __PRETTY_FUNCTION__ << std::endl; return *this; }
A& operator=( A &&) { std::cerr << __PRETTY_FUNCTION__ << std::endl; return *this; }
};
struct B : A
{
using A::A;
using A::operator=;
B& operator=(const B &) { std::cerr << __PRETTY_FUNCTION__ << std::endl; return *this; }
B& operator=( B &&) { std::cerr << __PRETTY_FUNCTION__ << std::endl; return *this; }
};
int main()
{
A a;
B b;
b = a; // OK
B b1( a ); // compile error
B b2(std::move(a)); // compile error
return 0;
}
Inheriting assignment operator using using keyword works OK, but inheriting copy and move constructors causes a compilation error: an inherited constructor is not a candidate for initialization from an expression of the same or derived type.
http://coliru.stacked-crooked.com/a/fe84b429c391c894:
main.cpp:16:14: note: an inherited constructor is not a candidate for initialization from an expression of the same or derived type
main.cpp:8:5: note: candidate: A::A(A&&)
A( A &&) { std::cerr << __PRETTY_FUNCTION__ << std::endl; }
^
main.cpp:16:14: note: inherited here
using A::A;
Why can I inherit assignment operator but cannot inherit copy constructor? What is a difference? I could understand if I couldn't inherit assignment operators too. But inheriting assignment operators in contrary is considered OK. That is a little strange for me.
The story
What I want is similar to what is asked in this question: I want to just add new methods to existing class without modifying it (it's a class from another library).
http://coliru.stacked-crooked.com/a/149a6194717cd465:
#include <iostream>
struct A // not my class
{
};
struct B : A
{
using A::A;
using A::operator=;
void foo() { std::cerr << "fuu" << std::endl; }
};
A NotMyFunc()
{
return {};
}
int main()
{
B b(NotMyFunc());
b.foo();
return 0;
}
But I don't want to reimplement copy and move constructors.
You need a constructor for B that has A as parameter. Then you need to make the default constructor explicit.
struct B : A
{
using A::A;
using A::operator=;
B() = default;
B(const A& a) : A(a) {}
B(A &&a): A(std::move(a)) {}
};
update:
class foo {
public:
foo() : x_(0) { std::cout << "foo constructor\n"; }
foo(foo& c) : x_(c.x_) { std::cout << "foo copy- constructor\n"; }
foo& operator=(foo const& c) {
std::cout << "foo operator=\n";
x_ = c.x_;
return *this;
}
protected:
int x_;
};
class bar : public foo {
public:
bar(int x) { foo::x_ = x; std::cout << "bar constructor\n"; }
bar(bar& c) { std::cout << "bar copy- constructor\n"; }
bar& operator=(bar const& c) {
foo::operator=(c);
std::cout << "bar operator=\n";
return *this;
}
};
int main() {
foo f;
bar b(123);
std::cout << "f = *static_cast<foo*>(&b);\n";
f = *static_cast<foo*>(&b); // no copy constructor is called
std::cout << "f = static_cast<foo>(b);\n";
f = static_cast<foo>(b); // copy constructor and assignment is called
}
output:
g++ -std=c++11 -Wall -pedantic -o main main.cpp && ./main
foo constructor
foo constructor
bar constructor
f = *static_cast<foo*>(&b);
foo operator=
f = static_cast<foo>(b);
foo copy- constructor
foo operator=
Is there an disatvantage of doing the static_cast like *static_cast<foo*>(&b); which is not calling the copy constructor as you can see in the output. see working example here.
In the now completely different question:
f = *static_cast<foo*>(&b);
is equivalent to
f = b;
which calls foo& operator=(foo const& c) with no additional temporaries. However,
f = static_cast<foo>(b);
is equivalent to:
f = foo(b);
or
{
foo temporary(b); // calls foo(foo const& );
f = temporary; // calls foo& operator=(foo const& );
}
which does create a temporary. Definitely prefer to do just:
f = b;
Given the now-functioning code, this line:
base b = *static_cast<base*>(&d);
Is exactly equivalent to these two:
base b = static_cast<b&>(d);
base b = d;
In every case, we're calling base(base const&) with no temporary copies. d is just taken as a const base&.
There is a downside of doing this construction (outside of static_cast just adding noise), which is that b is just a base object. It is not of type derived. So if you had some virtual function base::foo that derived provided an override of, b.foo() would call base::foo. This is known as slicing. If you want to mantain the derived-ness through the assignment, you have to keep a reference or pointer:
base& b = d;
base* b = &d;
Is there an disatvantage of doing the static_cast like *static_cast<foo*>(&b);
Yes. It's unnecessary noise. Just do this:
f = b;
It has the exact same effect.
#include <iostream>
using namespace std;
struct A
{
A() {}
A(const A &a) {
cout << "copy constructor" << endl;
}
A& operator=(const A &a) {
cout << "assigment operator" << endl;
}
A(A &&a) {
cout << "move" << endl;
}
A& operator=(A &&a) {
cout << "move" << endl;
}
};
struct B {
A a;
};
B func() {
B b;
return b;
}
int main() {
B b = func();
}
This prints "copy constructor".
For class B the move constructor and the move assignment operator should be automatic generated correct? But why is it using the copy constructor of class A and not the move constructor?
For me it doesn't print anything at all because the copy/move has been elided. However if I thwart RVO with something like:
extern bool choice;
B func() {
B b1, b2;
if (choice)
return b1;
return b2;
}
Then it prints:
move
It may be that your compiler does not yet implement the automatic generation of the move members.