Sample program:
#include <iostream>
#include <string>
#include <vector>
template <typename T>
void print(const T& _vec)
{
for( auto c: _vec )
std::cout << c << ",";
}
typedef std::vector<std::string> vecstr_t;
struct Trade
{
explicit Trade(vecstr_t&& vec) : _vec(vec )
{
}
vecstr_t _vec;
};
int main()
{
vecstr_t tmpV = {"ONE", "TWO", "THREE", "FOUR"};
std::cout << "size 1:" << tmpV.size() << "\t"; print(tmpV); std::cout << "\n" ;
Trade t(std::move(tmpV));
std::cout << "size 2:" << tmpV.size() << "\t"; print(tmpV); std::cout << "\n" ; // expted tmpV should be e,pty but it has original contents
print(t._vec);
}
I expect size 2: should be ZERO but output is:
size 1:4 ONE,TWO,THREE,FOUR,
size 2:4 ONE,TWO,THREE,FOUR,
ONE,TWO,THREE,FOUR,
explicit Trade(vecstr_t&& vec) : _vec(vec)
{}
In the constructor above, even though vec is of type rvalue reference to vecstr_t, it is itself an lvalue. The basic rule to remember is - if it has a name, it's an lvalue.
There are very few contexts where an lvalue may automatically be moved from (such as the return statement of a function that returns an object by value), but a constructor's mem-initializer list is not one of them.
In your example, _vec is copy constructed from vec. If you want it to be move constructed instead, use std::move.
explicit Trade(vecstr_t&& vec) : _vec(std::move(vec))
{}
Now the second call to print will not print anything. Note that technically the second call could print a non-zero size because the contents of a moved from vector are unspecified. But on most (probably all) implementations, you'll see an empty vector.
Live demo
Your comment below says your intent is to accept both rvalues and lvalues, move only in the case of the former, and copy the argument otherwise. As currently written, your constructor will only accept rvalues, and not lvalues. There are a few different options to achieve what you want.
The easiest probably is to change the parameter so that it's taking the argument by value, and then unconditionally move.
explicit Trade(vecstr_t vec) : _vec(std::move(vec))
{}
The drawback with this approach is that you may incur an additional move construction of the vector, but move constructing a vector is cheap, and you should go with this option in most cases.
The second option is to create two overloads of the constructor
explicit Trade(vecstr_t&& vec) : _vec(std::move(vec)) {}
explicit Trade(vecstr_t const& vec) : _vec(vec) {}
The drawback with this one is that the number of overloads will increase exponentially as the number of constructor arguments increases.
The third option is to use perfect forwarding.
template<typename V>
explicit Trade(V&& vec) : _vec(std::forward<V>(vec)) {}
The code above will preserve the value category of the argument passed to the constructor when it forwards it to construct _vec. This means that if vec is an rvalue, the vecstr_t move constructor will be called. And if it is an lvalue, it will be copied from.
The drawback with this solution is that your constructor will accept any type of argument, not just a vecstr_t, and then the move/copy construction in the mem-initializer list will fail if the argument is not convertible to vecstr_t. This may result in error messages that are confusing to the user.
Related
From this link it states
For example, in the code that we began with, my_vec.push_back("foo")
constructs a temporary string from the string literal, and then moves
that string into the container, whereas my_vec.emplace_back("foo")
just constructs the string directly in the container, avoiding the
extra move. For more expensive types, this may be a reason to use
emplace_back() instead of push_back(), despite the readability and
safety costs, but then again it may not. Very often the performance
difference just won’t matter
So I decided to try that and this is what i did
class foo
{
public:
int counter;
foo()
{
std::cout << "Regular constructor\n";
}
foo(const foo& f)
{
std::cout << "Copy constructor\n";
}
foo(foo&& f)
{
std::cout << "Move constructor\n";
}
};
int main()
{
std::vector<foo> f;
f.push_back(foo()); //Regular constructor and Move Constructor
f.emplace_back(foo()); //Regular constructor and Move Constructor
}
I noticed that both push_back and emplace_back behave similarly. I was thinking that emplace_back will only be calling the regular constructor based on what I read since it will be constructed in the vector stack.
vector::emplace_back(Args&&...) takes the arguments of the constructor you want to construct your new object with. In your quoted example this is const char* for the constructor string::string(const char*). In your own code you're forcing the move constructor by passing a temporary object. To default-construct your object in-place use f.emplace_back() without any arguments as the default constructor takes none.
Also to avoid reallocation (potentially more moves that would spoil your test) ensure the vector has space for your two test objects first using f.reserve(2).
Full code:
class foo
{
public:
foo()
{
std::cout << "Default constructor\n";
}
foo(const foo& f)
{
std::cout << "Copy constructor\n";
}
foo(foo&& f)
{
std::cout << "Move constructor\n";
}
};
int main()
{
std::vector<foo> f;
f.reserve(2);
f.push_back(foo());
f.emplace_back();
}
Output is
Default constructor
Move constructor
Default constructor
in C++, if a method is accepting left reference + pointer only,
it seems it suffices if we only have a template method with T& as its parameter, why we usually overload with test(T* ) as well ?
proof of concept: left reference method can take pointer argument.
#include <iostream>
using namespace std;
template<class T>
void test(T& arg) {
T value = arg;
cout << *value << endl;
}
int main() {
int b = 4;
int* a = &b;
test(a); // compiles and runs without issue.
return 0;
}
Why [do] we usually overload with test(T* ) as well?
I am not sure that we usually do anything of the sort, but if one were to overload for a pointer, it would be because pointers behave differently than object types. Remember, a pointer in fact is not an object but an address to an object.
The reason that test(a) compiles and runs without issue is because it is accepting a reference to a pointer to an object as its parameter. Thus, when the line cout << *value << endl; executes, the pointer is dereferenced back to an object and we see 4 printed to standard out.
As #HolyBlackCat mentioned, we usually want do different things for T& and T*.
As indicated in the example, for test(T&) we usually need to manually do dereference, this would result in the difference in the behavior, so it makes sense to have a overload like this.
I have a std::vector<double> that I have to move to a boost::container::flat_set<double>.
Both containers are contiguous, so after sorting the vector in principle I could move the data from one to the other.
Is there a way to move the whole data between these two different containers?
Please, take into account that I want to move the whole data, not element by element.
I can move data between containers of the same type, but not between different containers.
std::vector<double> v1 = ...
std::sort(v1.begin(), v1.end());
std::vector<double> v2(std::move(v1)); // ok
boost::flat_set<double> f2(v1.begin(), v1.end()); // doesn't move, it copies
boost::flat_set<double> f3(std::move(v1)); // doesn't compile
It seems that for this to work flat_set should have a move constructor from containers with .data(), where the pointer is stolen from the argument.
I believe there is some way to verify whenever data alignment in both containers match and memcpy could be used (and source cleared without destructing) exists and maybe someone will share it with us, but as long as we want to use STL there is a way: the std::move_iterator. It makes your container constructor move elements instead of copying. It does not remove elements out of source container though, but leaves them stateless (e.g. empty strings as in example).
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <boost/container/flat_set.hpp>
int main()
{
std::vector<std::string> v1 = {"a","v","d"};
std::sort(v1.begin(), v1.end());
std::vector<std::string> v2(std::move(v1)); // ok
boost::container::flat_set<std::string> f1(std::make_move_iterator(v2.begin()), std::make_move_iterator(v2.end())); // moves, but does not remove elements from of source container
for(auto& s : v1)
std::cout << "'" << s << "'" << ' ';
std::cout << " <- v1 \n";
for(auto& s : v2)
std::cout << "'" << s << "'" << ' ';
std::cout << " <- v2 \n";
for(auto& s : f1)
std::cout << "'" << s << "'" << ' ';
std::cout << " <- f1 \n";
}
Output
<- v1
'' '' '' <- v2
'a' 'd' 'v' <- f1
Online code: https://wandbox.org/permlink/ZLbocXKdqYHT0zYi
It looks like it is not possible without modifying the constructor boost::container::flat.
Without modifying either class it seems that the only a hack would do it, for example using reinterpret_cast.
The solution I found is either to use an alternative implementation of vector or very ugly code.
Before going into my solution, I must that say that this is probably a
defect of both classes. These clases should have a set of
release()/aquire(start, end) functions that respectively
returns the pointer range to the data releasing the ownership and
gets the pointer range owning it from then on. An alternative could be to
have a constructor that moves from any other container that has a the
data member function.
Solution using reinterpret_cast and a different implementation of vector
It turns out that reinterpret_casting from std::vector to boost::container::flat_set is not possible, because the layout is not compatible.
However it is possible to reinterpret_cast from boost::container::vector to boost::container::flat_set out of the box (that is because they have a common implementation).
#include<cassert>
#include<boost/container/flat_set.hpp>
int main(){
boost::container::vector<double> v = {1.,2.,3.};
boost::container::flat_set<double> fs = std::move(reinterpret_cast<boost::container::flat_set<double>&>(v));
assert(v.size() == 0);
assert(*fs.find(2.) == 2.);s
assert(fs.find(4.) == fs.end());
}
So, I can replace std::vector by boost::container::vector and I can move data to a flat_set.
Non-portable solution using std::vector and ugly code
The reason the layout of std::vector and boost::container::vector are different is that boost::container::vector stores metadata in this way:
class boost::container::vector{
pointer m_start;
size_type m_size;
size_type m_capacity;
}
while std::vector (in GCC) is basically pure pointers,
class std::vector{
pointer _M_start;
pointer _M_finish;
pointer _M_end_of_storage;
}
So, my conclusion is that moving is possible only through a hack given that the implementation I use of std::vector is not compatible with boost::container::flat_set.
In an extreme case, one can do this (sorry if this code offends someone, the code is not portable):
template<class T>
boost::container::flat_set<T> to_flat_set(std::vector<T>&& from){
// struct dummy_vector{T* start; T* finish; T* end_storarge;}&
// dfrom = reinterpret_cast<dummy_vector&>(from);
boost::container::flat_set<T> ret;
struct dummy_flat_set{T* start; std::size_t size; std::size_t capacity;}&
dret = reinterpret_cast<dummy_flat_set&>(ret);
dret = {from.data(), from.size(), from.capacity()};
// dfrom.start = dfrom.finish = dfrom.end_storarge = nullptr;
new (&from) std::vector<T>();
return ret;
};
int main(){
std::vector<double> v = {1.,2.,3.};
boost::container::flat_set<double> fs = to_flat_set(std::move(v));
assert(v.size() == 0);
assert(*fs.find(2.) == 2.);
assert(fs.find(4.) == fs.end());
}
Note that I am not taking into account allocator issues at all. I am not sure how to handle allocators here.
In retrospect I don't mind using a form of cast for this specific problem, because somehow I have to tell that the vector is sorted before moving to flat_set. (The problem is that this goes to extreme because it is a reinterpret_cast.)
However this is a secondary issue, there should be legal way to move from std::vector to boost::container::vector.
Today I found that this code doesn't work as I expect it to work.
According to my knowledg for L-values copy constructor should be called while for R-values it should choose move constructor. Otherwise what's the purpose of std::move which actually does nothing but casts to R-value. I was expecting that return obj will call copy constructor, but it calls move.
I understand that copy is useless here, but that's about rules. What if my copy constructor has a side effect and that's my case (I know that it shouldn't, but technically it can - for example here: std::cout call).
Is there anything in standard that allows such behavior? Also how can I force a copy?
#include <iostream>
class X
{
public:
X() = default;
X(const X& r): i(r.i)
{
std::cout << "copy ctor" << std::endl;
}
X(const X&& r): i(r.i)
{
std::cout << "move ctor" << std::endl;
}
int i = 0;
};
X foo()
{
X obj;
obj.i = 10;
return obj;
}
int main()
{
X x = foo();
}
move ctor
move ctor
From cppreference (emphasis mine):
If [the returned expression] is an lvalue expression and the conditions for copy elision are met, or would be met, except that [the returned expression] names a function parameter, then overload resolution to select the constructor to use for initialization of the returned value is performed twice: first as if [the returned expression] were an rvalue expression (thus it may select the move constructor or a copy constructor taking reference to const), and if no suitable conversion is available, overload resolution is performed the second time, with lvalue [returned expression] (so it may select the copy constructor taking a reference to non-const).
The above rule applies even if the function return type is different from the type of [the returned expression] (copy elision requires same type)
Long story short, return implicitly tries to move what you return when it makes sense. It will only copy as a last resort (no move constructor available, for example).
Short version:
I need to pass a template class a parameter pack, which is the result of applying a function to another parameter pack. This needs to work within a using statement.
Background:
As a challenge, I'm writing a generic C++11 version of python's zip(). In order to do so, I have written a generic zipIterator template class which can be used to iterate over many iterators simultaneously, yielding a tuples of their values. For example:
#include "zip.hpp"
#include <iostream>
#include <vector>
#include <tuple>
int main(){
std::vector<int> vec = {0,1,2,3};
char arr[] = {'a','b', 'c'};
zipIterator<decltype(vec.begin()), char*, decltype(vec.rbegin())>
itr(vec.begin(), std::begin(arr), vec.rbegin());
zipIterator<decltype(vec.begin()), char*, decltype(vec.rbegin())>
end(vec.end(), std::end(arr), vec.rend());
for(; itr!=end; ++itr){
std::cout << "(" << std::get<0>(*itr) << ", " << std::get<1>(*itr)
<< ", " << std::get<2>(*itr) << ")" << std::endl;
}
}
//output:
//(0, a, 3)
//(1, b, 2)
//(2, c, 1)
The Problem
I would like to make a zip container class which can be passed containers, and which zips over them by calling std::begin() and std::end() on each one. So far I have this:
template<typename... Containers>
class zip{
public:
using iterator = zipIterator<???>;
zip(Containers... cs) : begin_(iterator(std::begin(cs)...)),
end_(iterator(std::end(cs)...)){};
iterator begin() {return begin_;}
iterator end() {return end_;}
private:
iterator begin_;
iterator end_;
};
My question is: what goes in the place of ??? to make this work? So far I have tried
std::begin(std::declval<Containers>())...,
decltype(std::begin(Containers)...),
std::result_of<std::begin(Containers)>::type...,
and many more variations on this.
Sorry if this is a repeat. I read the following Stack Overflow answers and they all seem to be related, but I don't think they are quite what I am looking for:
C++11 call member function on template parameter pack of base classes if present
How to make generic computations over heterogeneous argument packs of a variadic template function?
Calling a function for each variadic template argument and an array
using iterator = zipIterator<decltype(std::begin(std::declval<Containers&>()))...>;
The basic idea is that ... expands the pattern on its left. Here, the pattern is decltype(std::begin(std::declval<Containers&>())) - the type of the return value of std::begin when called on an lvalue of type Containers.