My confusion lies at line Cube c = a;. I think it should call both default constructor and copy constructor, but in fact it only calls copy constructor.
Isn't Cube c, just like Cube a, a new object that should invoke default constructor?
class Cube
{
public:
int length_;
Cube(){
length_ = 1;
cout<< "Default Constr"<< endl;
}
Cube(const Cube & obj){
length_ = obj.length_;
cout<< "Copy Constr"<< endl;
}
};
Cube foo(){
Cube c;
return c;
}
int main() {
Cube a; //invoke default constructor only
Cube c = a; //invoke copy constructor only
return 0;
}
As others have pointed out, what you have here is copy initialization, so there's no way a default constructor would be used.
There is a slightly different case in which copy initialization can (at least theoretically) involve an extra step. Consider code like this:
class foo {
public:
foo(int) {}
};
int main() {
foo f = 1;
}
In this case, (at least before C++17) there were theoretically supposed to be two separate constructors involved. First, a temporary was constructed, initialized with 1, then the copy constructor was called to initialize f from that temporary object.
In this case, most compilers will generate code that just directly initializes f from q, so it's equivalent to foo f{1};. The compiler is still required to respect the fact that a copy is needed though, so if you delete the copy constructor, compilation will fail:
class foo {
foo(foo const &)= delete;
public:
foo(int) {}
};
int main() {
foo f = 1;
}
Result (with gcc):
trash9.cpp: In function 'int main()':
trash9.cpp:8:17: error: use of deleted function 'foo::foo(const foo&)'
foo f = 1;
^
trash9.cpp:2:9: note: declared here
foo(foo const &) = delete;
^~~
trash9.cpp:4:9: note: after user-defined conversion: 'foo::foo(int)'
foo(int) {}
^~~
But changes in the rules starting with C++17 mean that now even that is allowed (so if I add -std=c++17 to the compilation above, it succeeds).
When you write:
Cube c = a;
This is actually not an assignment but a copy-initialization.
In your case, it would be the same as if you had written:
Cube c(a);
Only the copy-constructor is called.
Keep in mind that an object is constructed only once, therefore only one constructor.
The default constructor is called.
It is used to initialize the object.
Then the copy constructor is used to set the state of the new object to be the same with the initial one.
Related
Consider:
std::shared_ptr<Res> ptr=new Res();
The above statement doesn't work. The compiler complains there isn't any viable conversion....
While the below works
std::shared_ptr<Res> ptr{new Res()} ;
How is it possible?!
Actually, what are the main differences in the constructor{uniform, parentheses, assignments}?
The constructor of std::shared_ptr taking a raw pointer is marked as explicit; that's why = does not allow you to invoke this constructor.
Using std::shared_ptr<Res> ptr{new Res()}; is syntax that allows you to call the an explicit constructor to initialize the variable.
std::shared_ptr<Res> ptr=new Res();
would invoke an implicit constructor taking a pointer to Res as single parameter, but not an explicit one.
Here's a simplified example using a custom class with no assignment operators and just a single constructor:
class Test
{
public:
Test(int i) { }
// mark all automatically generated constructors and assignment operators as deleted
Test(Test&&) = delete;
Test& operator=(Test&&) = delete;
};
int main()
{
Test t = 1;
}
Now change the constructor to
explicit Test(int i) { }
and the main function will no longer compile; the following alternatives would still be viable:
Test t(1);
Test t2{1};
Recently, I came across this answer which describes how to initialize a std::array of non-default-constructible elements. I was not so surprised because that answer clearly doesn't do any default-constructing.
Instead, it is constructing a temporary std::array using aggregate initialization, then moving (if the move constructor is available) or copying into the named variable when the function returns. So we only need either the move constructor or copy constructor to be available.
Or so I thought...
Then came this piece of code which confounded me:
struct foo {
int x;
foo(int x) : x(x) {}
foo() = delete;
foo(const foo&) = delete;
foo& operator=(const foo&) = delete;
foo(foo&&) = delete;
foo& operator=(foo&&) = delete;
};
foo make_foo(int x) {
return foo(x);
}
int main() {
foo f = make_foo(1);
foo g(make_foo(2));
}
All the five special member constructors/operators are explicitly deleted, so now I shouldn't be able to construct my object from a return value, correct?
Wrong.
To my surprise, this compiles in gcc (with C++17)!
Why does this compile? Clearly to return a foo from the function make_foo(), we have to construct a foo. Which means that in the main() function we are assigning or constructing a foo from the returned foo. How is that possible?!
Welcome to the wonderful world of guaranteed copy elision (new to C++17. See also this question).
foo make_foo(int x) {
return foo(x);
}
int main() {
foo f = make_foo(1);
foo g(make_foo(2));
}
In all of these cases, you're initializing a foo from a prvalue of type foo, so we just ignore all the intermediate objects and directly initialize the outermost object from the actual initializer. This is exactly equivalent to:
foo f(1);
foo g(2);
We don't even consider move constructors here - so the fact that they're deleted doesn't matter. The specific rule is [dcl.init]/17.6.1 - it's only after this point that we consider the constructors and perform overload resolution.
Notice that pre-C++17 (before guaranteed copy elision), you might already return that object with braced-init-lists:
foo make_foo(int x) {
return {x}; // Require non explicit foo(int).
// Doesn't copy/move.
}
But usage would be different:
foo&& f = make_foo(1);
foo&& g(make_foo(2));
I have two classes one of them has an object of another class as a data member and it's constructor accepts the class object to initialize the data member object.
class x{
public:
x(int a, int b)
{ cout << a << b;}
};
class y{
x temp;
y(x& o){ this-> temp = o;}
};
But compiler shows an error in y::y(x&): no matching function to call x::x()
I am using codeblocks 16.01
You have defined the constructor:
x(int a, int b)
in x. This means that the compiler will no longer define any constructors for you, this includes the x() constructor. So you can only construct x with x(int, int). Here in your code:
x temp;
y(x& o) { // < No initializer list
You attempt to default construct x, but x has no default constructor! Either define one, or construct x in the initializer list with the constructor you have provided.
For example:
y(x& o) : x(0, 0) {
But you will create your object then you will use the implicitly defined copy-assignment operator to assign it, which is a bit of a waste of time. You can actually solve all these problems by using the copy-constructor:
class x{
...
x(const x ©) { // Define a copy constructor or just use
// the implicitly defined one.
Then in y, just use it in y's initializater list:
x temp;
y(x& o) : temp(o) {}
y(x& o){ this-> temp = o; }
is not very C++ idiomatic.
I. As a rule, you should avoid requiring more access rights than needed. Here, you're likely to not mutate the construction argument, so you don't need to pass it by a mutable reference:
y(x const &o);
II. Member initialization is done very differently in C++:
y(x const &o): temp(o) {}
When you write
y(x const &o) { temp = o; } // please avoid writing `this->'
then what happens is: first, temp is constructed and default-initialized (prior to the opening brace); then, inside the braces, temp is already a valid object, so what follows is a copy-assignment. In your case, x is not default-constructible, so compilation fails.
I played around with explicit constructors and their behavior, so I created this class:
#include <iostream>
class X
{
public:
explicit X(void)
{
std::cout << "Default constructor\n";
}
explicit X(X const& x)
{
std::cout << "Copy constructor\n";
}
explicit X(X&& x)
{
std::cout << "Move constructor\n";
}
};
Which is basically just a stub to to test explicit constructors.
Then I wanted to try several situations. So I tried this:
X foo(void)
{
X a{};
return a; // ERROR: no matching constructor found!
}
int main()
{
X w{}; // Default Constructor
X x{w}; // Copy Constructor
X y{std::move(x)}; // Move Constructor
X z{foo()};
}
And as you can see I can't return a inside foo(). I know it tries to initialize the return type Foo with the copy constructor, but for some reason it can't use it.
How come it can't use my provided copy constructor? I know that the explicit specification causes the problem, because when I remove it from the copy constructor it works. But why?
What confuses me even more is that I can do the following:
void bar(const X& a) { /* */ }
bar(X{});
It doesn't complain. But shouldn't bar() construct it's parameter a the same way foo() constructs its return type?
When you are returning from foo:
X foo()
{
X a{};
return a;
}
You are implicitly copying a into the return of foo. But the copy constructor is marked explicit, so that is disallowed.
I'm not sure there is ever a reason to mark the copy/move constructors explicit.
I think you've misunderstood the meaning of explicit. An explicit constructor WILL NOT BE USED FOR IMPLICIT TYPE CONVERSIONS/CASTS. This means the
X foo(void){
X a{};
return a; // ERROR: no matching constructor found!
}
won't compile since you've already told the compiler not to use the copy constructor implicitly.
I reckon that what you want to achieve is "move" rather than copy a. As long as there is a (normal) move constructor in your class, a will be moved rather than copied anyway - this is the default behaviour. Actually, even with c++99, most compilers are clever enough to optimise out this copy anyway.
You can't because you said you didn't want the compiler to use it implicitly when you declared it explicit.
explicit X(X const& x)
But x has to be copied into the return value. Just change it to
X(X const& x)
and everything will work.
Live on Coliru
#include <type_traits>
struct A {};
struct B {
B(A& a) : a(a) {}
//B& operator=(B const& b) = default; // LINE 1
//B& operator=(B const& b) { a = b.a; return *this; } // LINE 2
A& a;
};
static_assert(std::is_copy_constructible<B>::value, ""); // pass
static_assert(std::is_copy_assignable<B>::value, ""); // fail
int main() {
A a;
B b(a);
return 0;
}
Compiler: g++ 5.1
The default copy constructor is fine, but the default copy assignment operator fails (even with LINE 1 uncommented).
Declaring it explicitly (uncommenting LINE 2) works though. Is it a bug?
No, it's not a bug. Your class doesn't have a defined copy assignment operator, because you have a reference member. If you have a class with reference semantics, it is not obvious whether you would want assignment to just assign from one reference to another (which results in copy assigning the referenced object), or whether the reference should be rebound (something you can't actually do, although this is usually what you would want). So the standard simply doesn't generate a default assignment operator, but allows you to define one manually with the semantics you want.