C++ - call assignment operator at creation instead of copy constructor - c++

I want to enforce explicit conversion between structs kind of like native types:
int i1;
i1 = some_float; // this generates a warning
i1 = int(some_float): // this is OK
int i3 = some_float; // this generates a warning
I thought to use an assignment operator and copy constructor to do the same thing, but the behavior is different:
Struct s1;
s1 = other_struct; // this calls the assignment operator which generates my warning
s1 = Struct(other_struct) // this calls the copy constructor to generate a new Struct and then passes that new instance to s1's assignment operator
Struct s3 = other_struct; // this calls the COPY CONSTRUCTOR and succeeds with no warning
Are there any tricks to get that third case Struct s3 = other_struct; construct s3 with the default constructor and then call the assignment operator?
This all compiles and runs as it should. The default behavior of C++ is to call the copy constructor instead of the assignment operator when you create a new instance and call the copy constructor at once, (i.e. MyStruct s = other_struct;becomes MyStruct s(other_struct); not MyStruct s; s = other_struct;. I'm just wondering if there are any tricks to get around that.
EDIT: The "explicit" keyword is just what I needed!
class foo {
foo(const foo& f) { ... }
explicit foo(const bar& b) { ... }
foo& operator =(const foo& f) { ... }
};
foo f;
bar b;
foo f2 = f; // this works
foo f3 = b; // this doesn't, thanks to the explicit keyword!
foo f4 = foo(b); // this works - you're forced to do an "explicit conversion"

Disclaimer: I'm ready to take the downvotes on this, since this doesn't answer the question. But this could be useful to the OP.
I think it is a very bad idea to think of the copy constructor as default construction + assignment. It is the other way around:
struct some_struct
{
some_struct(); // If you want a default constructor, fine
some_struct(some_struct const&); // Implement it in the most natural way
some_struct(foo const&); // Implement it in the most natural way
void swap(some_struct&) throw(); // Implement it in the most efficient way
// Google "copy and swap idiom" for this one
some_struct& operator=(some_struct x) { x.swap(*this); return *this; }
// Same idea
some_struct& operator=(foo const& x)
{
some_struct tmp(x);
tmp.swap(*this);
return *this;
}
};
Implementing things that way is fool proof, and is the best you can obtain in term of conversion semantics in C++, so it is the way to go here.

You can get around this if you overload the type cast operator for other_struct, and edit the original structure accordingly. That said, it's extremely messy and there generally isn't a good reason to do so.
#include <iostream>
using namespace std;
struct bar;
struct foo {
explicit foo() {
cout << "In foo default constructor." << endl;
}
explicit foo(bar const &) {
cout << "In foo 'bar' contructor." << endl;
}
foo(foo const &) {
cout << "In foo constructor." << endl;
}
foo const & operator=(bar const &) {
cout << "In foo = operator." << endl;
return *this;
}
};
struct bar {
operator foo() {
cout << "In bar cast overload." << endl;
foo x;
x = *this;
return x;
}
};
int main() {
bar b;
foo f = b;
return 0;
}
Outputs:
In bar cast overload.
In foo default constructor.
In foo = operator.
In foo constructor.
In foo constructor.

In short, no.
The long version...actually that's about it. That's just not how it works. Had to come up with something to fill the character requirement though.

I don't think so. When you write
Struct s3 = other_struct;
It looks like an assignment, but really it's just declarative syntax that calls a constructor.

Related

Why converting constructor needs copy constructor to be declared when explicit cast performed?

From what I learned, I thought Foo a = 1 is equivalent to Foo a = (Foo)1.
With copy constructor declared, yes, they both result in calling Converting constructor.
class Foo
{
public:
// Converting constructor
Foo(int n) : value_(n) { std::cout << "Converting constructor." << std::endl; }
// Copy constructor
Foo(const Foo&) { std::couut << "Copy constructor." << std::endl; }
private:
int value_;
};
int main()
{
Foo a = 1; // OK - prints only "Converting constructor."
Foo b = (Foo)1; // OK - prints only "Converting constructor."
}
In contrast, without copy constructor, it doesn't compile even though it doesn't ever call copy constructor.
class Foo
{
public:
// Converting constructor
Foo(int n) : value_(n) { std::cout << "Converting constructor." << std::endl; }
// Copy constructor deleted
Foo(const Foo&) = delete;
private:
int value_;
};
int main()
{
Foo a = 1; // OK - prints only "Converting constructor."
Foo b = (Foo)1; // Error C2280: 'Foo::Foo(const Foo &)': attempting to reference a deleted function
}
What makes difference?
Thank you.
Foo a = 1; calls Foo(int n). (Foo)1 also calls Foo(int n). Foo b = (Foo)1; calls copy constructor Foo(const Foo&). Until C++17 the last call can be elided but the copy constructor must be available. Since C++17 copy elision is mandatory and the copy constructor isn't necessary: https://en.cppreference.com/w/cpp/language/copy_elision
Probably you're using a C++ standard before C++17 and the copy constructor is required but not called.
"From what I learned, I thought Foo a = 1 is equivalent to Foo a = (Foo)1." That's not equivalent. The first is one constructor call and the second is two constructor calls but one call can/must be elided.

Return a reference to std::variant that holds non-copyable alternatives

Let's consider this code:
#include <iostream>
#include <variant>
struct Foo {
#if 0
Foo() = default;
Foo(const Foo&) = delete;
#endif
void foo() { std::cout << "foo" << '\n'; }
};
using MyVariant = std::variant<Foo>;
MyVariant& getVariant() {
static MyVariant variant{Foo{}};
return variant;
}
int main() {
auto& variant = getVariant();
auto& foo = std::get<Foo>(variant);
foo.foo();
}
It compiles and prints "foo".
If you change the #if 0 by #if 1, making Foo a non copyable class, then the code no longer compiles.
Why do I want Foo to be non copyable? To avoid errors "unexpected copies" with something like auto variant = getVariant(); (note the that & is missing compared to the original code).
I can't fin the correct syntax for this code. Maybe there is none... My purpose is to select an alternative the first time the function is called, and the variant will not be modified later (it will always hold the same alternative).
What do you suggest? Thanks!
Statement MyVariant variant{Foo{}}; requires Foo to have an available copy or move constructor, but there isn't one. When you delete the copy constructor that also makes the move constructor non-declared, so that you need to declare that move constructor explicitly for to compile.
auto foo = std::get<Foo>(variant) requires a copy constructor. Use a reference instead.
A couple of these fixes to make it compile:
struct Foo {
Foo() = default;
Foo(const Foo&) = delete;
Foo(Foo&&) = default; // <--- Declare move constructor.
void foo() { std::cout << "foo" << '\n'; }
};
using MyVariant = std::variant<Foo>;
MyVariant& getVariant() {
static MyVariant variant{Foo{}};
return variant;
}
int main() {
auto& variant = getVariant();
auto& foo = std::get<Foo>(variant); // <--- Use reference.
foo.foo();
}

C++ reference can be assignable?

I've been messing with references wrapped in container classes. Why is the following code legal and appear to behave correctly?
#include <iostream>
class Foo
{
public:
Foo( int i ) : i_( i ) {}
int i_;
};
class FooWrapper
{
public:
FooWrapper( Foo& foo ) : foo_( foo ) {}
Foo& foo_;
};
int main( int argc, char* argv[] )
{
Foo foo( 42 );
FooWrapper fw( foo );
FooWrapper fw2 = fw;
std::cout << fw2.foo_.i_ << std::endl;
return 0;
}
In the absence of an explicit operator=, I believe C++ does a memberwise copy. So when I do FooWrapper fw2 = fw;, is this not two operations: (1) create FooWrapper fw with a default ctor followed by (2) assignment from fw to fw2? I'm sure this isn't happening, because a reference can't be created uninitialized, so is the creation of fw2 actually treated as a copy construction?
I'm sometimes unclear on the roles of copy construction vs. assignment; they seem to bleed into each other sometimes, as in this example, but there's probably some rule I'm unaware of. I'd be grateful for an explanation.
Despite the = in the syntax, FooWrapper fw2 = fw; copy constructs fw2 (using the copy constructor). Neither default construction nor assignment is involved at all.
And to answer the question in the title: no, references cannot be assigned. If you wrote code that attempted to actually default construct or assign a FooWrapper, such as:
FooWrapper fw2;
fw2 = fw;
...this would fail. Officially, only a "diagnostic" is required. Unofficially, every compiler I know of would/will refuse to compile it.
In the line below, you are constructing fw2 by making it a copy of fw. That is, you are invoking the copy-constructor.
FooWrapper fw2 = fw;
Example
Here is an (online) example on how default constructor, copy constructor, copy assignment operator, and move assignment operator (from C++11) are invoked.
#include <iostream>
struct foo
{
foo() {std::cout << "Default constructor" << '\n';}
foo(foo const&) {std::cout << "Copy constructor" << '\n';}
foo& operator=(foo const&) {std::cout << "Copy assignment operator" << '\n'; return *this; }
foo& operator=(foo &&) {std::cout << "Move assignment operator" << '\n'; return *this; }
};
int main( int argc, char* argv[] )
{
foo a; // Default constructor
foo b = a; // Copy constructor
foo c; // Default constructor
c = b; // Copy assignment operator
b = std::move(c); // Move assignment operator
}
This initialization actually calls the copy constructor.
FooWrapper fw2 = fw;
and is equivalent to
FooWrapper fw2(fw);
The implicitly defined copy constructor doesn't need to create an uninitialized reference
For reference:
http://en.cppreference.com/w/cpp/language/copy_constructor

Defining both Assignment operator and copy constructor

Suppose I have a class A. I have defined a copy constructor and an assignment operator overloading function. When I do
Class A;
Class B=A;
Then while defining Class B, is the copy constructor invoked or the assignment operator?
Thanks in advance.
EDIT:
Sorry, I mentioned wrong code. It should be:
A a;
A b=a;
IIRC T t = value invokes the copy constructor, you can verify that by outputting a string in the constructors to determine which method is used. IIRC when the declaration and assignment are on the same line, it is not called assignment but initialization.
On the other hand, what you've posted does not make sense, you cannot assign one type to another type, you can only assign to type instances.
EDIT: Even if you have a case of two different types (the context of your question is not clear on this one):
class A {};
class B {
public:
B(const A& other) { cout << "copy"; }
B& operator=(const A& other) { cout << "assign"; }
};
int main() {
A a;
B b = a; // copy con
B b1(a); // same as above
b = a; // assign op
}
Even then, when both the "copy constructor" and assignment operators take in another type, the copy constructor will still be invoked rather than the assignment operator.
Assuming you actually mean something like:
A a;
A b = a;
The copy constructor is invoked. The Standard allows = this special meaning in this usage.
Create a simple class with both, and debug what function is executed by setting a breakpoint in both. Then youll see, and youll also learn a little bit of debugging.
Let's try it out!
#include <iostream>
class A {
public:
A() {
std::cout << "default constructor\n";
}
A(const A& other) {
std::cout << "copy constructor\n";
}
A& operator=(const A& rhs) {
std::cout << "assignment operator\n";
return *this;
}
};
int main(int argc, char** argv) {
std::cout << "first declaration: ";
A a;
std::cout << "second declaration: ";
A b(a);
std::cout << "third declaration: ";
A c = a;
std::cout << "fourth declaration: ";
A d;
std::cout << "copying? ";
d = a;
return 0;
}
This prints:
first declaration: default constructor
second declaration: copy constructor
third declaration: copy constructor
fourth declaration: default constructor
copying? assignment operator
Working example here: http://codepad.org/DNCpqK2E
when you are defining a new object with assigning another object into it. It invokes copy constructor.
e.g,
Copy constructor call:
Class A;
Class B=A;
Assignment operator call:
Class A;
Class B;
B=A;
You can always test this, by writing a "print" statement in both the methods to find out which one is being called.
Hope it helped...

A question about auto_ptr

template<class Y>
operator auto_ptr_ref<Y>() throw() {
return auto_ptr_ref<Y>(release());
}
It is part of implementation of class auto_ptr in standard library.
What does this means to do?
Why there is an "auto_ptr_ref" between "operator" and "()"?
I'll tell you why that conversion operator happens to be there. Well, look at this example:
struct A;
struct B {
explicit B(A&a):a(a){ }
A &a;
};
struct A {
A() { }
A(B b){ move_from(a); }
A(A &a) { move_from(a); }
operator B() { return B(*this); }
void move_from(A &a) {
std::cout << "A::A(#" << &b.a << ")" << std::endl;
}
};
int main() {
A a = A();
}
We have move semantics for our class A: In its copy constructor, we want to "steal" away some stuff from the other instance. For auto_ptr, that is the pointer that is managed, for us we just output a message instead. What is important is that we can't use a usual copy constructor:
A(A const& a) {
/* oops, a is const, we can't steal something from it! */
}
But if we change that to A(A &a), we won't be able to construct from a by-value/temporary A: Those can't be bound to a reference-to-nonconst:
A(A &a) {
}
...
A a = A(); // fail, because A &a = A() doesn't work
auto_ptr and our A class uses the trick that non-const member functions can still be called on temporaries/by-value A's. That is, we could also have written:
struct A {
...
B get_b() { return B(*this); }
...
};
...
A a = A().get_b();
But that works, we don't want to be bothered with that, of course. We want that it just works to assign a A() or the return value of a function returning an A by-value. So what auto_ptr and our A class uses is a conversion operator, which automatically figures out that when A is converted to B, then we could construct a A instance using the B we created temporarily.
That is the conversion operator in action, casting from auto_ptr to auto_ptr_ref<Y>.