可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I only have access to C++03 and I often want to move a vector into a function the way you can do it in C++11. The question how to do it not to confuse the user of the code too much. So my question is how did programmers do it before C++11.
I know that vector can be "moved" using swap function. So here is what I have come up with:
class Foo
{
public:
Foo(std::vector<int>& vec)
{
using std::swap;
swap(vec, m_vec); // "move" vec into member vector
}
private:
std::vector<int> m_vec;
};
// usage:
std::vector<int> v(100, 1337);
Foo foo(v);
// v.empty() == true
The problem with this approach is that its not evident to the user that their vector will be moved into the class Foo. Is there a best practice solution to this kind of problem? Thanks in advance!
回答1:
You could define a type wrapping a reference, and a function to wrap it, to give something similar to move semantics at the call site. Something along the lines of
template <typename T> struct move_ref {
explicit move_ref(T & ref) : ref(ref) {}
T & ref;
};
template <typename T> move_ref<T> move(T & t) {return move_ref<T>(t);}
Foo(move_ref< std::vector<int> > vec)
{
using std::swap;
swap(vec.ref, m_vec); // "move" vec into member vector
}
Foo foo(move(v));
Alternatively, Boost has a library to allow move semantics without C++11.
回答2:
You may use some wrapper with explicit name:
template <typename T>
class MyMove
{
public:
explicit MyMove(T& t) : t(t) {}
T& get() {return t;}
private:
T& t;
};
template <typename T>
MyMove<T> myMove(T& t) { return MyMove<T>(t); }
And then
class Foo
{
public:
Foo(MyMove<std::vector<int>> vec)
{
using std::swap;
swap(vec.get(), m_vec); // "move" vec into member vector
}
private:
std::vector<int> m_vec;
};
usage:
std::vector<int> v(100, 1337);
Foo foo(myMove(v));
回答3:
Sure it is possible to have move semantics in C++03.
Using Boost.Move:
#include <vector>
#include <utility>
#include <boost/move/move.hpp>
class Foo
{
public:
Foo(BOOST_RV_REF(std::vector<int>) vec)
{
std::swap(vec, m_vec); // "move" vec into member vector
}
private:
std::vector<int> m_vec;
};
int main()
{
std::vector<int> v(100, 1337);
Foo foo(boost::move(v));
}
Or you can write it on your own, that works with l-values, temporaries, and where r_value_ref<T>
wrapper can be used as T&
or const T&
thanks to operator T&()
:
#include <vector>
#include <utility>
template <typename T>
class r_value_ref
{
public:
explicit r_value_ref(const T& t) : t(t) {}
T& get() const
{
return const_cast<T&>(t);
}
operator T&() const
{
return const_cast<T&>(t);
}
private:
const T& t;
};
template <typename T>
r_value_ref<T> my_move(const T& t)
{
return r_value_ref<T>(t);
}
class Foo
{
public:
Foo(r_value_ref<std::vector<int> > vec)
{
m_vec.swap(vec); // no .get() required !
// or std::swap(vec.get(), m_vec);
}
private:
std::vector<int> m_vec;
};
int main()
{
Foo foo_from_r_value(my_move(std::vector<int>(100, 1337)));
std::vector<int> v2(100, 1337);
Foo foo_from_l_value(my_move(v2));
}
Live demo link
However, it's unsafe, e.g. r_value_ref<int> i(123); i.get() = 456;
compiles successfully, but leads to undefined behavior. Hand-written r-value-reference wrappers require a little more effort to make them perfectly safe.
回答4:
If I get you right, you want to "move" the vector in a class member via a constructor call.
In C++11 you would have to provide a constructor with std::vector<T>&&
argument and call
Foo my_foo(std::move(my_vector));
In C++03 you can add a "named constructor" or a friend which does this job for you.
template <class T>
class Foo
{
public:
static Foo<T> move_from_vector(std::vector<T> & vector)
{
Foo<T> new_foo;
new_foo.m_vec.swap(vector);
return new_foo;
}
private:
std::vector<T> m_vec;
};
Where you could use it like this:
int main()
{
std::vector<int> h(5);
Foo<int> g(Foo<int>::move_from_vector(h));
}
In this way it is clear that the vector is moved and the move-syntax does not differ that much from C++11 (although being non-generic of course).
Note that this will copy Foo
if optimization is turned off.
回答5:
C++03 way is to use std::auto_ptr to express passing ownership of the data
class Foo
{
public:
explicit Foo(std::auto_ptr<std::vector<int> > vec)
: m_vec(vec)
{
}
private:
Foo(const Foo&);
Foo& operator=(const Foo&);
std::auto_ptr<std::vector<int> > m_vec;
};
// usage:
std::auto_ptr<std::vector<int> > v(new std::vector<int>(100, 1337));
Foo foo(v);
// v.get() == 0
std::auto_ptr in signature clearly states that data is passed inside the function, leaving call site with empty pointer, due to the nature of copying an auto_ptr.
Note the need to either disallow or explicitly define correct copy-constructing and assignment of such a class, since default copy constructor and assignment of auto_ptr is usually not what would be correct.