Are the std::move at the end of the following function needed (to avoid copying the vectors)?
std::pair<vector<int>, vector<int>> f()
{
vector<int> a, b;
...
return std::make_pair(std::move(a), std::move(b));
}
Or will the compiler move the vectors even if the std::move are omitted?
The std::moves are necessary.
If what you suggest were allowed, the following code would be (surprisingly) broken since both arguments to std::make_pair would be moved-from:
std::vector<int> a;
// populate a
return std::make_pair(a, a);
Related
I came across a function a colleague had written that accepted an initializer list of std::vectors. I have simplified the code for demonstration:
int sum(const std::initializer_list<std::vector<int>> list)
{
int tot = 0;
for (auto &v : list)
{
tot += v.size();
}
return tot;
}
Such a function would allow you call the function like this with the curly braces for the initializer list:
std::vector<int> v1(50, 1);
std::vector<int> v2(75, 2);
int sum1 = sum({ v1, v2 });
That looks neat but doesn't this involve copying the vectors to create the initializer list? Wouldn't it be more efficient to have a function that takes a vector or vectors? That would involve less copying since you can move the vectors. Something like this:
int sum(const std::vector<std::vector<int>> &list)
{
int tot = 0;
for (auto &v : list)
{
tot += v.size();
}
return tot;
}
std::vector<std::vector<int>> vlist;
vlist.reserve(2);
vlist.push_back(std::move(v1));
vlist.push_back(std::move(v2));
int tot = sum2(vlist);
Passing by initializer list could be useful for scalar types like int and float, but I think it should be avoided for types like std::vector to avoid unnecessary copying. Best to use std::initializer_list for constructors as it intended?
That looks neat but doesn't this involve copying the vectors to create the initializer list?
Yes, that is correct.
Wouldn't it be more efficient to have a function that takes a vector or vectors?
If you are willing to move the contents of v1 and v2 to a std::vector<std::vector<int>>, you could do the samething when using std::initializer_list too.
std::vector<int> v1(50, 1);
std::vector<int> v2(75, 2);
int sum1 = sum({ std::move(v1), std::move(v2) });
In other words, you can use either approach to get the same effect.
Let's say I have a vector containing a struct with a member describing its target vector.
struct Foo
{
int target;
static const int A = 0;
static const int B = 1;
static const int C = 2;
};
std::vector<Foo> elements;
std::vector<Foo> As;
std::vector<Foo> Bs;
std::vector<Foo> Cs;
std::vector<Foo> others;
Now I want to move each Foo in one of the four other vectors based on the value of Target.
For example
auto elements = std::vector<Foo>{ {Foo::A}, {Foo::A}, {Foo::B} };
Should result in two elements in As, one in Bs and none in Cs or others. Elements should be empty afterwards.
I could as well do it myself, but I wonder if there is an STL algorithm I could use to do its job.
Standard algorithms usually don't operate on multiple output destinations, so it's hard to come up with a suitable solution here when you want to abstract away the destination containers through output iterators. What might come closest is std::copy_if. This could look like
// Help predicate creation:
auto pred = [](int target){ return [target](const Foo& f){ return f.target == target; }; };
std::copy_if(elements.begin(), elements.end(), std::back_inserter(As), pred(Foo::A));
std::copy_if(elements.begin(), elements.end(), std::back_inserter(Bs), pred(Foo::B));
std::copy_if(elements.begin(), elements.end(), std::back_inserter(Cs), pred(Foo::C));
std::copy_if(elements.begin(), elements.end(), std::back_inserter(others),
[](const Foo& f){ return false; /* TODO */ });
elements.clear();
If copying is more expensive than move-construction, you should pass std::make_move_iterator(elements.begin()) and the same for elements.end() to the algorithm. The issue here is that this doesn't scale. std::copy_if linearly traverses the input range, and the above has to do this four times. One traversal can be obtained e.g. like the following.
auto doTheWork = [&As, &Bs, &Cs, &others](const Foo& foo) {
if (foo.target == Foo::A)
As.push_back(foo);
else if (foo.target == Foo::B)
Bs.push_back(foo);
else if (foo.target == Foo::C)
Cs.push_back(foo);
else
others.push_back(foo);
};
std::for_each(elements.begin(), elements.end(), doTheWork);
In this scenario, we have at least employed a standard algorithm, but shifted the logic into a rather ugly lambda. Note that the above lambda will always copy its arguments, it needs some adjustments to properly work with std::move_iterators.
Sometimes, a good old range based for loop is the most readable solution.
I want to add a Foo object to a std::vector but I don't want to create a temporary object to add to the vector because that would call Foo::~Foo() once the temporary object goes out of scope. Do I have to use new and make the vector store Foo pointers, or is there another way?
What I don't want to do:
void FooHandler::AddFoo(int a, int b, int c) {
Foo foo(a, b, c);
vectorOfFoos.push_back(foo);
} //foo goes out of scope so Foo::~Foo() is called
Would these work?
//Foo has an implicit constructor which takes a FooSettings object
struct FooSettings {
public:
int a;
int b;
int c;
};
void FooHandler::AddFoo(int a, int b, int c) {
vectorOfFoos.push_back(Foo(a, b, c));
} //is Foo::~Foo() called here?
void FooHandler::AddFoo(FooSettings settings) {
vectorOfFoos.push_back(settings);
} //is Foo::~Foo() called here?
Both of your solutions will involve the creation of a temporary. You can use emplace_back instead of push_back to construct the Foo instance in-place rather than copying it in to the vector.
void FooHandler::AddFoo(int a, int b, int c) {
vectorOfFoos.emplace_back(a,b,c);
}
If I have a C++ class with copy and move constructors and copy assignment like so:
class Foo {
public:
int A[];
// custom ctor
Foo(size_t N) {
A = new A[N];
for(int i = 0; i < N; i++) A[i] = i * i;
}
// copy ctor
Foo(Foo& other) {
size_t N = sizeof(other.A) / sizeof(int);
A = new A[N];
for(int i = 0; i < N; i++) A[i] = other.A[i];
}
// move ctor
Foo(Foo&& other) {
A = other.A;
other.A = nullptr;
}
// copy assignment AND move assignment?
Foo& operator=(Foo other) {
std::swap(this.A, other.A);
return *this;
}
// default dtor
~Foo() {
delete[] A;
}
Can I in this case simply avoid defining a move assignment operator and assume that still move assignment is taking place when possible?
My reasoning behind that is: the copy assignment operator has to construct the Foo object defined as parameter. Now it can choose between the copy ctor and the move ctor as both are available. Thus if given an rvalue it should choose the move ctor and if given an lvalue it should choose the copy ctor. Is this correct?
Also: will this implementation work regarding the sizeof calculation of the array? If not, why not?
Recommendation: Use std::vector
Problem: int A[]; is a flexible array member, which gets its storage from extra memory adjacent to the main object. You can't do A = new int[N]; nor sizeof (A).
Fix: You can use int* A; and keep track of the size yourself, because sizeof a pointer isn't related to the size of the array it points to.
Better: std::vector<int> remembers its size and implements destructor, move, and copy operations so you don't have to.
Regarding the question about implementing assignment using pass-by-value: Yes, that's legal, it's even an idiom: "copy-and-swap". However, it will cause an unnecessary extra move in the move assignment case, so you may want to implement that directly. Or not, since two moves should barely cost more than the required one.
If you use std::vector to store your data, the compiler-generated copy and move operations will do the right thing automatically.
You can always don't define the move ctor and assignment operators. All you will lose is opportunity of performance gains. So no, no need for move assignment.
In your case, this example Foo f = Foo(); f = Foo() you're using the normal assignment operator, where you could be doing a move. You will do that in two moves instead plus the swap.
About you sizeof question, it wont work. sizeof will return the size of the pointer type, which is constantEDIT: except for VLA, where it performs a run-time check, so it is correct.
I read here and also here and examples on cpluplus.com
And I still don't understand how it works
what confuses me at most how lambdas work with _if algorithms like copy_if that they don't reference containers in the body
std::vector<int> foo = {25,15,5,-5,-15};
std::vector<int> bar (foo.size());
// copy only positive numbers:
auto it = std::copy_if (foo.begin(), foo.end(), bar.begin(), [](int i)
{return !(i<0);} )
doesn't reference vector object foo in the body. so how it performs the desired action?
Additionally, I don't understand what is the difference beetween capturing a variable and passing as parameter
I also tried my own example:
vector<unsigned long>v2(10);
for(unsigned long i=0;i<v2.size();i++)
v2[i]=10-i;
v2.erase(remove_if(v1.begin(), v1.end(),[](unsigned long n) {
return n%2==1; } ),v2.end());//remove odd numbers
It compiles (MVS 2010 with Intel Composer 14) but produces rubbish and assertion error.
If you look at std::copy_if source code, you should see how that works.
Essentially what copy_if does is this:
void copy_if(It1 f1, It1 l1, It2 f2, Pred pred) {
for (; f1 != l1; ++f1) {
if (pred(*f1)) {
*f2 = *f1;
++f2;
}
}
}
It does not know about containers, nor does it have to. The pair of iterators (f1, l1) specify a range where the items are copied from, the iterators contain all the knowledge about how to get to the element. The iterator f2 specifies the starting point of the destination range. If the destination range is not big enough woul will have a buffer overflow bug. The predicate is a function that indicates whether to copy an element or not. It does not have to know anything about the container, it just needs to be able to tell copy_if whether an element being visited should be copied or not.
Difference between capturing a variable and passing as argument should be explained by the following snippets. This lambda
int captured = 42;
auto functor = [captured](int x) { return x%2 == 0; };
essentially means this:
int captured = 42;
struct Blah
{
Blah(int c) : captured_(c) { }
bool operator()(int x) const { return x%2 == 0; }
int captured_;
} functor(captured);
You're erasing from vector v2 using iterators from container v1. This is expected to.give you crap results - it's undefined behaviour.