7

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!

rozina
  • 3,714
  • 21
  • 43
  • You can name your function `Steal_Vector` instead of `Foo`. – pqnet Aug 26 '14 at 14:13
  • 3
    @pqnet Probably not since it's a constructor. – Quentin Aug 26 '14 at 14:14
  • I don't know if this was done, but it would require detailed documentation. I think it would clearer if there was a function `Foo::swap_buffer` instead. – eerorika Aug 26 '14 at 14:14
  • @pqnet I taught of that as well. But a lot of times you want to pass the vector in the constructor. And I dislike making a default constructor and then calling a member function 'steal_vector()' :) – rozina Aug 26 '14 at 14:14
  • It is strange to write whole line `using std::swap;` instead of `std::swap(...)` At least I understand why people do write `using namespace std;` - because they are lazy to type std:: every time. But here it is even more typing the first place. – Slava Aug 26 '14 at 14:29
  • @Slava True in this case. I just got used to writing it this way for other times, when there is a swap specialized swap for a class that is not part of std namespace. In case you haven't heard of swap idiom, then this will clear it up: http://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom – rozina Aug 26 '14 at 14:32
  • @rozina 'The expected way to make a user-defined type swappable is to provide a non-member function swap in the same namespace as the type: see Swappable for details.' see here http://en.cppreference.com/w/cpp/algorithm/swap – Slava Aug 26 '14 at 14:35
  • @Slava I would have used the `swap` method of the `vector` class anyway. Would that have been a bad idea? – pqnet Aug 26 '14 at 14:37
  • @Slava Yes, but when you call swap you write using 'std::swap' before calling swap. And now I just always do, since its never wrong :) I don't mind if I type a few extra chars. – rozina Aug 26 '14 at 14:37

5 Answers5

5

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));
Jarod42
  • 173,454
  • 13
  • 146
  • 250
  • I would mark the argument of `MyMove()` as `const` and run a `const_cast` in there, so that it works with temporary values as well. It is clear by the name that we are going to modify the given value anyway. – pqnet Aug 26 '14 at 14:32
  • It doesn't work with temporaries: `Foo foo(myMove(std::vector(100, 1337)))` – Piotr Skotnicki Aug 26 '14 at 14:34
  • @PiotrS. Temporeries can only bind to const references right? In that case I doubt there will be a C++03 solution that will work with that :) – rozina Aug 26 '14 at 14:35
  • 1
    @rozina: it can hold `const T& t;` and then `T& get() {return const_cast(t);}` – Piotr Skotnicki Aug 26 '14 at 14:38
  • @rozina you can use a `const &` parameter and `const_cast` to strip away constness, which is probably what boost does in its implementation – pqnet Aug 26 '14 at 14:39
  • 1
    Note that the member `get()` has to be marked `const` because otherwise you will not be able to use it from a temporary variable – pqnet Aug 26 '14 at 14:40
  • @PiotrS. True good idea. Seems like c++03 can have move semantics. Which makes me quite happy :) Will test this and Mike's solution tomorrow and see which one fits me best and accept the answer :) – rozina Aug 26 '14 at 14:41
  • @rozina: see my answer below – Piotr Skotnicki Aug 26 '14 at 14:43
  • @pqnet you can use non-`const` methods from temporary objects all you want. The old `MyClass() = MyClass()` trick even lets you create a non-`const` reference to a temporary without casting ;) – Yakk - Adam Nevraumont Aug 26 '14 at 14:46
  • 2
    Using `const_cast` (as suggested) may result in UB if done on const object (but it allows usage of temporary variable). – Jarod42 Aug 26 '14 at 14:53
5

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.

Mike Seymour
  • 235,407
  • 25
  • 414
  • 617
  • Very C++11 like, I like it! Are there any major draw backs to using this? I ask because I searched for this using google many times and never got a nice solutions like this. Was this approach used before C++11? – rozina Aug 26 '14 at 14:31
  • 1
    @rozina Don't call it `move`, call it `please_move` or something distinct from the name in C++11. Also, consider storing a pointer instead of a reference, and think about assignment semantics. – Yakk - Adam Nevraumont Aug 26 '14 at 14:45
  • 2
    @Yakk: It *is* called something distinct from C++11's `std::move`, unless you foolishly start polluting whichever namespace it's in; and there's no need for assignment, since it's only intended to be used as a temporary function argument. – Mike Seymour Aug 26 '14 at 15:10
  • @MikeSeymour *nod*, so block assignment. – Yakk - Adam Nevraumont Aug 26 '14 at 15:17
  • @Yakk: It is blocked, due to the non-assignable member. (Although, even if it weren't, there wouldn't really be any point in jumping through hoops to prevent an unintended but harmless use of the class.) – Mike Seymour Aug 26 '14 at 15:23
  • This version does not work with temporaries does it? Though looking at std::move it does not take a const reference either. – rozina Aug 26 '14 at 15:25
  • @rozina: No, this version won't work with temporaries. (In C++11, you wouldn't use `move` on a temporary, since it's already an _rvalue_, but I don't think you can emulate that behaviour.) – Mike Seymour Aug 26 '14 at 15:32
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.

Piotr Skotnicki
  • 43,267
  • 7
  • 101
  • 142
  • 1
    I'm not sure I would call sane any environment which allows usage of boost libraries without allowing c++11 – pqnet Aug 26 '14 at 14:21
  • 4
    @pqnet: Why not? Some environments are stuck with old compilers (with no C++11 support) for a variety of reasons; there's no particular reason to avoid Boost in such a situation, and good reason to use libraries like this to provide the missing features. – Mike Seymour Aug 26 '14 at 14:23
  • 1
    @pqnet why not? one usually uses boost when you *don't* have c++1 – Paul Evans Aug 26 '14 at 14:23
  • @pqnet: Boost.Move utilizes real r-value references when compiled as C++11 – Piotr Skotnicki Aug 26 '14 at 14:24
  • Thanks man. I forgot to mention that yes, I don't have access to Boost either. Embedded project.. :) I think Mike Seymour's solution is basically same as this though without boost? – rozina Aug 26 '14 at 14:25
  • @rozina yes, either Mike or Jarod's answer are the same I think – pqnet Aug 26 '14 at 14:26
  • @PiotrS. I would add an automatic conversion from `r_value_ref` to `const T&` so that if a move constructor has not been defined for `Foo` it will fallback to the copy constructor, as in C++11 – pqnet Aug 26 '14 at 14:54
  • 1
    Beware that, by supporting temporaries, you've lost `const`-correctness. `Foo foo_from_const(my_move(const_v))` will attempt to modify a `const` object, with no compile-time warning. – Mike Seymour Aug 26 '14 at 15:34
  • @MikeSeymour: I don't get your point, `std::move()` allows this as well: `std::vector&& ref = std::move(const_v);` – Piotr Skotnicki Aug 26 '14 at 15:43
  • @PiotrS.: [No it doesn't](http://ideone.com/5IhNXQ). `std::move` acts like a `static_cast`, which can't remove `const` or `volatile` qualifiers. Your use of `const_cast` will remove those qualifiers, losing const-correctness. – Mike Seymour Aug 26 '14 at 15:48
  • @MikeSeymour: nah.. my mistake, it works of course the other way: `T&&` can be assigned to `const T&` – Piotr Skotnicki Aug 26 '14 at 15:50
1

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.

Pixelchemist
  • 21,390
  • 6
  • 40
  • 70
  • 1
    `template` in `template `, fix it – Piotr Skotnicki Aug 26 '14 at 14:26
  • Wouldn't it just copy the vector in the end? What is the difference between implementing `move_vector_to_foo` as you did and with `{ return vector; }`? – pqnet Aug 26 '14 at 14:27
  • @pqnet The compiler should not copy `Foo` here (because of return value optimization / copy elision). – Pixelchemist Aug 26 '14 at 14:33
  • @Pixelchemist but this depends on compiler, and is not guaranteed by language. And it will not happen if you have a custom defined copy constructor – pqnet Aug 26 '14 at 14:34
  • 1
    @pqnet Any decent compiler will omit the copy if optimizations are turned on. (And it will elide the copy even if a custom copy constructor is present since the standard explicitly allows to skip side effects.) – Pixelchemist Aug 26 '14 at 14:50
  • Copy ellision is almost guaranteed these days on almost every compiler, even in C++03. FWIW, C++17 is going to codify it as part of the official standard as well. – NHDaly Jun 28 '16 at 19:22
0

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.

Andrei
  • 9
  • 1
  • 3