How to copy a built-in array via copy-constructor? - c++

We know that a built-in array can neither be copied nor be assigned. So If it is a member data of a class/struct/union It is OK to leave the compiler do its magic to copy them:
struct ArrInt5{
ArrInt5() = default;
ArrInt5(ArrInt5 const&) = default; // OK
int a[5];
};
ArrInt5 a, b = a; // OK
Sometimes that is not the case for example if the array holds objects of non-default- constructible objects. in that
case we do need to define our copy-ctor to do the job:
struct Bar{
// deleted default ctor
Bar(int x) : x_(x){}
int x_ = 0;
};
struct Foo{
Foo();
Foo(Foo const&);
Bar arr_[5];
};
Foo::Foo() : arr_{0, 1, 2, 3, 4}
{}
Foo::Foo(Foo const& rhs) : arr_{rhs.arr_[0], rhs.arr_[1], rhs.arr_[2], rhs.arr_[3], rhs.arr_[4]}
{}
As you can see Foo has a built-in array of five objects of type struct Bar that type is not default-constructible so the default ctor and the copy ctor must initialize it (arr_).
The problem is: How could initialize that array if it is of a big size lets say 1 million element? Should I hard copy them element by element? or there's some workaround?
I know so many will recommend me to use the equivalent STL container std::array but I'm not on that topic, I'm asking whether there's a workaround for my built-in array of non default-constructible objects.

The only way to generate this effect for you automatically is to wrap your array in a class type of some kind that has a compiler-generated copy constructor (e.g. such as a defaulted constructor). In your particular example, you could just have Foo(const Foo&) be defaulted:
struct Foo{
Foo();
Foo(Foo const&) = default;
// This generates your:
// "Foo::Foo(Foo const& rhs) : arr_{rhs.arr_[0], rhs.arr_[1], rhs.arr_[2], rhs.arr_[3], rhs.arr_[4]}"
// by the compiler
Bar arr_[5];
};
However the above only works if your copy-logic is trivial. For more complex copying, you can't always have a default copy constructor -- but we can solve this more generally.
If you wrap an array data member of any type/size in a struct or class and make sure it has compiler-generated copy constructors, then copying is easy in a constructor.
For example:
template <typename T, std::size_t N>
struct Array {
...
T data[N];
};
This allows for the simple case of copying:
Array<Bar,1000000> a = {{...}};
Array<Bar,1000000> b = a;
Or the more complex case of copying in a constructor that might require more logic than the compiler-generated ones:
class Foo {
...
Foo(const Foo& other);
...
Array<Bar,100000> arr_;
};
Foo::Foo(const Foo& other)
: arr_{other.arr_}
{
... other complex copy logic ...
}
At which point, congratulations -- you have invented std::array!
std::array was added to the standard library exactly because it was this simple to introduce a library-solution to this problem without having to alter the language itself. Don't shy away from it.

Related

Swapping class with unique_ptr

#include <memory>
#include <algorithm>
using namespace std;
class A {
public:
unique_ptr<int> u;
A(){}
A(const A& other): u(new int(*other.u)){} // If I comment this out, it works.
// A(A&&){} // This does not help.
};
int main() {
A a;
A b = a;
swap(a, b);
}
This code does not work - fails with a wall of template errors saying no matching function for call to ‘swap(A&, A&)’. Why? Deleting the second constructor helps, but I need it in other code. I guessed it could be connected with automatical deleting of some constructors when other are defined, but manually adding move constructor does not help either. How can I fix this?
std::swap() requires its arguments to be move-constructible and move-assignable.
Given:
struct A {
unique_ptr<int> u;
A();
};
A is swappable, thanks to the implicitly defined move-constructor and move-assignment operators.
But, given:
struct A {
unique_ptr<int> u;
A();
A(A const&);
};
By declaring a user defined copy constructor, you're inhibiting the implicit definition of A's move constructor and move assignment operator
(and by having a non-assignable-copyable member, you're inhibiting the implicit generation of A's copy constructor and copy assignment operator).
To make A swappable again, you need to either user-define both (*):
struct A {
unique_ptr<int> u;
A();
A(A const&);
A(A&&);
A& operator=(A&&);
};
or neither, and just add proper copy-assignment:
struct A {
unique_ptr<int> u;
A();
A(A const&);
A& operator=(A const&);
};
but, this will probably defeat your original intent of avoiding deep copies on swap, so you'll probably end up defining all of them anyway.
(*) noexcept specifications omitted just for brevity ...

Delegating copy constructor and const data initialization

I have a class A with lots of data members, some of which are constant. All data members have proper copy constructors, so I want to default a copy constructor of my class:
class A
{
public:
A() : a(1) {}
A(const A& op) = default;
private:
// ... Lots of constant and non-constant member data ...
const int a;
};
Then, I want to write a constructor which accepts a reference to A and a value which should initialize one of constant data members:
A(const A& op, const int a_);
Here op should be copied, and a should be initialized with a_ after that or instead of copying. I would like to avoid manual initialization of all data members by delegating to a copy constructor, but how to overwrite my const data member in this case?
For example:
// Illegal: constructor delegation can't be mixed with field initialization.
A(const A& op, const int a_) : A(op), a(a_) {}
// Illegal: attempt to assign constant member.
A(const A& op, const int a_) : A(op) { a = a_; }
// Hack. May lead to UB in some cases.
A(const A& op, const int a_) : A(op)
{
*(const_cast<int*>(&a)) = a_;
// ... or same tricks with memcpy ...
}
Obviously, all of these approaches are evil as they try to initialize a twice.
Another solution is to move all constant data to a base class and write all needed ctors, but it looks to verbose.
Is there a cleaner way to implement A(const A&, int a_)?
Unfortunately, C++ const field initialization is a very special case with specific syntax, so is constructor delegation, and the constructor syntax has no provision to mix them, so there can be no clean and neat solution here (at least for current C++ versions, maybe later...). The best I can imagine is:
class A
{
public:
A() : a(1) {}
A(const A& op):
const_field1(op.const_field1),..., a(op.a) // all const fields here
{ init() };
A(const A& op, int a_):
const_field1(op.const_field1),..., a(a)) // dup line at editor level
private:
void init(void) {
// init non const fields here
}
// ... Lots of constant and non-constant member data ...
const int a;
};
It does not make sense if you have only the copy ctor and one single additional one, but it could ease code maintenability if you have many additional ctors. It is a pity that only non const field setting can be factorized across different ctors with a private method, but C++ standard is like that.
What about a copy constructor with a full initialization list? Since your data is const, you will only be able to assign a value to it using initalization list.
A(const A& op, int a_) :
prop_a(op.prop_a_),
prop_b(op.prop_b_),
// continue for all members ...
a(a_) // except a
{
}

Is default move constructor optimized away by compiler?

I have an std::multimap of the following type:
typedef std::multimap<std::pair<bool,uint32_t>, FooObject>
The FooObject has default move copy and assign constructors declared:
FooObject(FooObject&& v) = default;
FooObject& operator=(FooObject&& other)=default;
The copy/assign constructors are private to disable implicit copy.
So I should be able to emplace a pair into the map like this:
mymap.emplace(std::make_pair(false,32),FooObject());
This throws a list of errors with the one at the end:
error C2660: 'std::pair::pair': function does not take
2 arguments
If I declare move copy assign constructors without "default"
then it compiles ok.
FooObject(FooObject&& v){}
FooObject& operator=(FooObject&& other){}
Why is that? Does the compiler optimize away these constructors when marked with "default" keyword? I am using MSVC140
UPDATE:
Based on the comments below I found the reason - FooObject has a non-copiable member instance.
Here is the FooObject:
#define NO_COPY_ASSIGN(TypeName) \
TypeName (const TypeName &); \
void operator= (const TypeName &);
class FooObject
{
private:
NO_COPY_ASSIGN(FooObject)
public:
struct FooStruct
{
FooBuffer frameBuffer; //<--Here it is
}fooStruct;
FooObject(){}
/** Move constructor to allow insert into vector without copy */
FooObject(FooObject&& v) = default;
FooObject& operator=(FooObject&& other) = default;
};
*FooBuffer has also its copy/assign private.But I still don't get why replacing 'default' with {} fixes that.Please explain.
Your problem is that one of the member of FooObject is not move-able, which prevents the compiler from generating default move operations.
The {} versions of the move operations you implement yourself do no work (specifically: they don't actually do a move operation) on the members of FooObject and are thus legal.
The difference between
FooObject(FooObject&& v) = default;
and
FooObject(FooObject&& v){}
Is that the former will emit a constructor that moves each member from v while the latter default constructs each member and does nothing with v.
Since FooBuffer is not movable that means that the compiler will delete FooObject(FooObject&& v) = default; as it would be ill-formed.
With FooObject(FooObject&& v){} you do not have that problem as you never try to move the members of v. Since there is no member initialization list the compiler will add one for you that just default constructs the members.
You can see this behavior more explicitly with this:
struct Moveable
{
Moveable() = default;
Moveable(Moveable&&) { std::cout << "in Moveable(Moveable&&)\n"; }
};
struct Foo
{
Foo() = default;
Foo(Foo&&) = default;
Moveable m;
};
struct Bar
{
Bar() = default;
Bar(Bar&&){}
Moveable m;
};
int main()
{
Foo f;
Bar b;
std::cout << "test_f\n";
Foo test_f(std::move(f));
std::cout << "test_b\n";
Bar test_b(std::move(b));
}
which outputs
test_f
in Moveable(Moveable&&)
test_b
Live Example
Showing that nothing is actually moved in Bar's move constructor.

How to initialize parameters / return values whose type has no copy constructor and only explicit constructors

How to initialize function parameters or function return values when the type has no copy constructor and only explicit constructors? For example:
struct A { };
struct B {
B(const B&) = delete;
explicit B(const A&) {}
};
void foo(B) {}
B bar() {
A a;
return ...; // how to return a?
}
int main() {
A a;
foo( ... ); // how to pass a?
}
return a / foo(a) does not work because constructor B(const A&) is explicit. return B(a) / foo(B(a)) does not work because copy constructor is deleted.
My intention is to know the language rules. To me it looks like a flaw in the language that, considering the presented type B, I can initialize locals, statics and class members but apparently neither function parameters nor function return values.
B(const B&) = delete;, without an explicit definition of move-constructor, means the object cannot be copied or moved.
This means that you cannot pass by value, either as a parameter or as a return.
The explicit constructor is a red herring.
If you don't want to change B to enable copying or moving, then you will have to pass by reference.
answers in annotated code below:
struct A { };
struct B {
B(const B&) = delete;
B(B&&) = default; // how to return B - pt 1 : move constructor
explicit B(const A&) {}
};
void foo(B) {}
B bar() {
A a;
return B(a); // how to return B - pt 2 : explcitly convert it
}
int main() {
A a;
foo(B(a)); // how to pass a - explicit conversion
}
I think your issue is a misconception:
You do not initialize function parameters or return values with the constructor.
Instead you hand in parameters to a function, handing in means copying them.
Depending on the function signature either the variable itself is copied (void foo(B b)) or the reference/pointer to it is copied.
In the first case you need a copy constuctor for your type, while you do not need one for references/pointers.
The same is true for returning values (!) from a function.
You also need a copy constructor there.
But you deleted the copy constructor, thus the compile errors.
With C++11 move semantics were added as an optimisation opportunity.
When a move constructor is available r-values will be moved in/out of functions automatically instead of copied.
Returning a local variable will also move it out instead of copying.
Since you declared a copy consturctor (no matter if as deleted/default) no move related functions will be created by the compiler.
And thus also the automatic moving fails.

Other way to prohibit a certain C++ class construction except than declaring the constructor private?

Say I have a class with some const reference member variable and I would like to forbid a certain type of construction. So I would declare the according constructor private. Of course, a constructor must initialise all const reference member variables of the class. Doing so, however, results in odd looking code:
class A {
};
class B {
B(const A& a): host(a) {}
private:
B():host(A()) {} // This is ugly and not needed !!
const A& host;
};
Is there another way to prohibit a certain construction type except than declaring the constructor private? I do not want to let the compiler write a constructor for me.
Simply don't define this:
B():host(A()) {} // This is ugly and not needed !!
That is, the following should do what you want to do:
class B {
B(const A& a): host(a) {}
private:
//B():host(A()) {} // This is ugly and not needed !!
const A& host;
};
The idea is if you've defined a constructor that takes parameter(s), then the default constructor is not generated by the compiler. That means, instances of the above class cannot be default created!
B b1; //error - needs default constructor which doesn't exist!
B b2(a); //ok - only way to create an instance!
C++11 solution
In C++11, you can explicity tell the compiler not to generate a particular constructor as:
struct B
{
B(const A &a) {}
B() = delete; //disable
};
Not only that. There is more to it, as explained below:
Now the interesting part
You can also selectively disable constructor(s) for selected types which makes delete more interesting. Consider this,
struct A
{
A (int) {}
};
Object of this class can be created not only with int argument, but any type which implicitly converts to int. For example,
A a1(10); //ok
A a2('x'); //ok - char can convert to int implicitly
B b;
A a3(b); //ok - assume b provides user-defined conversion to int
Now suppose, for whatever reason, I don't want the users of class A to create objects with char or class B , which fortunately or unfortunately can implicitly convert to int, then you can disable them as:
struct A
{
A(int) {}
A(char) = delete; //disable
A(const B&) = delete; //disable
};
Now here you go:
A a1(10); //ok
A a2('x'); //error
B b;
A a3(b); //error - assume (even if) b provides user-defined conversion to int
Online Demo : http://ideone.com/EQl5R
The error messages are very clear:
prog.cpp:9:5: error: deleted function 'A::A(char)'
prog.cpp:10:5: error: deleted function 'A::A(const B&)'
Just leave it out. As soon as you provide a custom constructor, no other constructor is auto-generated (except for a copy constructor).
If you want to forbid any construction – ending up with a class that has only static members – you can simply declare the constructor as private, and not define it. Such a class is very rarely useful in C++ (since you cannot create instances of it); the only purpose that I can think of is to implement trait classes:
template <typename T>
struct type_to_color {
static char const* value() { return "blue"; }
private:
type_to_color();
};
template <>
struct type_to_color<int> {
// Integers are red!
static char const* value() { return "red"; }
private:
type_to_color();
}
char const* char_color = type_to_color<char>::value();
char const* int_color = type_to_color<int>::value();
However, this is extremely uncommon: trait classes are abundant in C++ but they never declare their constructors as private, it’s just assumed that everybody knows not to instantiate them.
I'll post the C++11 solution: delete the constructor.
class B {
B() = delete;
B(const A& a): host(a) {}
private:
const A& host;
};
As Konrad Rudolph sayd: as soon you provide a custom constructor, no other constructor is auto-generated (except for a copy constructor).
Therefore, other options are:
Declare the constructor private (so that you can't inherit from your class), but do not provide a definition:
class B {
public:
B(const A& a): host(a) {}
private:
B(); // not implemented!
const A& host;
};
Or in C++11, as R. Martinho Fernandes says:
class B {
public:
B() = delete;
B(const A& a): host(a) {}
private:
const A& host;
};