3

I have been reading the book "The C++ programing language 4th edition" by Bjarne Stroustrup (The creator of c++) and have been learning about move constructors and move assignments.

In the book for the class vector (see 1 for header below) he shows how to implement the move constructor (see 2 below) and says the move assignment is implemented in a similar manner but doesn't show how. I have implemented the move assignment myself (see 3 below) and everything seems to be working fine, however, I am not sure I have implemented it correctly.

I am not getting any errors and have looked at many examples but I cannot confirm its correct for my specific class. Can someone experienced with c++ please look at my code and comment if it is correct?

EDIT: Also please see 4 for constructors and destructor.

Thank you for your time.

P.S: Any helpful hints or modifications are welcome

1) Class Header File:

#ifndef VECTOR_H
#define VECTOR_H

#include <cstdlib>
#include <iostream>
#include <stdexcept>

using namespace std;

template<typename T>
class Vector {

public:
    // constructors
    Vector(int s);
    Vector(std::initializer_list<T>);

    // destructor
    ~Vector();

    // copy constructor and copy assignment
    Vector(Vector&);
    Vector<T>& operator=(Vector&);

    // move constructor and move assignment
    Vector(Vector&&);
    Vector<T>& operator=(Vector&&);

    // operators
    T& operator[](int);
    const T& operator[](int) const; // the second const means that this function cannot change the state of the class
                                    // we define operator[] the second time for vectors containing constant members;
    // accessors
    int getSize();


private:
    int size;
    T* elements;

};

#endif /* VECTOR_H */

2) Move constructor (implemented in the same way as book):

// move constructor 
template<typename T>
Vector<T>::Vector(Vector&& moveme) : size{moveme.size}, elements{moveme.elements}
{
    moveme.elements = nullptr;
    moveme.size = 0;
}

3) Move assignment (not sure if correct):

// move assignment
template<typename T>
Vector<T>& Vector<T>::operator=(Vector&& moveme) 
{
    delete[] elements; // delete old values
    elements = moveme.elements;
    size = moveme.size;
    moveme.elements = nullptr;
    moveme.size = 0;
    return *this;
}

4) Constructors and destructor:

#include <array>

#include "Vector.h"

// constructors
template<typename T>
Vector<T>::Vector(int s) {    
    if(s<0) throw length_error{"Vector::Vector(int s)"};
    // TODO: use Negative_size{} after learning how to write custom exceptions
    this->size = s;
    this->elements = new T[s];
}

template<typename T>
Vector<T>::Vector(std::initializer_list<T> list) : size(list.size()), 
        elements(new T[list.size()]) 
{
    copy(list.begin(), list.end(), elements);
}

// destructor
template<typename T>
Vector<T>::~Vector()
{
    delete[] this->elements;
}
Amr
  • 411
  • 6
  • 21
  • Whether this is correct, or not, depends entirely on what the regular constructors and the destructor do. Which you failed to show. Which makes it impossible to tell you the answer. And, finally, I highly doubt that your book told you to put `using namespace std;` in the header file, or anywhere at all, and to put the implementation in a translation unit, rather than the header file, which wouldn't work either. – Sam Varshavchik Sep 23 '17 at 13:21
  • I will post the full implementation shortly. The book doesn't even use a header file in its examples I've separated the implementation myself. Can you please tell me whats wrong with putting name space std in the header? (and yes he does use it in the book.) – Amr Sep 23 '17 at 13:24
  • See https://stackoverflow.com/questions/1452721/why-is-using-namespace-std-considered-bad-practice . P.S. Nobody wants to see a "full implementation" here. This is not a code review site. The only thing that must be posted is a [mcve]. – Sam Varshavchik Sep 23 '17 at 13:26
  • I edited and added the constructor and destructor. I have encountered what you have linked before - I agree its only there because the book uses it to save time. I will stop using it though. – Amr Sep 23 '17 at 13:29
  • 1
    The move assignment seems reasonable, except that putting templates in a cpp file makes them usable in that cpp file only. See [Why can templates only be implemented in the header file?](https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file) – Bo Persson Sep 23 '17 at 13:33
  • hmmm, interesting tibbet, thanks for linking. I'm assuming inluding a .cpp at the end of the header would also work but .tpp is just better practice ? – Amr Sep 23 '17 at 13:42
  • 1
    @hammeramr Putting template code in a *.cpp file is confusing to people who might expect it to be compilable, and also to some IDEs or build systems which might automatically try to actually compile it, which is pointless. – aschepler Sep 23 '17 at 13:44
  • In 2) you forgot the `std::move` – Post Self Sep 23 '17 at 14:56
  • @kim366 Thats how the book implements it. Can you clarify ? – Amr Sep 23 '17 at 14:58
  • @hammeramr Ok, then it's probably correct. I never used move constructors like that. But If I had a class member of Vector then I'd `std::move` it in the constructor `Vector::Vector(Vector&& moveme) : child_vector{std::move(moveme)}`, but it may work differently when accessing it's members – Post Self Sep 23 '17 at 15:06
  • @kim366 `std::move`ing fundamental types and pointers is a bit useless, because they are completely trivial to copy and move, unlike some other types (like `std::string`), where it is better to use `std::move`. But again, for such types, it doesn't really matter. – Rakete1111 Sep 23 '17 at 15:17
  • 1
    @hammeramr `std::move != move semantics`. You have move semantics, where rvalues can be moved (using the move constructor) instead of copied. `std::move` is just a facility to enable move semantics (like using the move constructor) for types that are not rvalues. – Rakete1111 Sep 23 '17 at 15:22
  • @Rakete1111 in your reply to kim366 I'm confused on which method is useless. So do you suggest using kim366 method or mine ? assuming they are equivalent. – Amr Sep 23 '17 at 15:29
  • 1
    @hammeramr The one with `std::move`. Just like using a `const&` for for example `int` as a parameter to a function. It doesn't matter from a performance standpoint because `int` is so small, it can be passed around in registers. Same thing for moving it. Use the one you like better - I don't use it, because it's less to type :P – Rakete1111 Sep 23 '17 at 15:32
  • @hammeramr Okay, I see. Always happy to learn :) (What do you need the move constructor for then, though?) – Post Self Sep 23 '17 at 15:53
  • @kim366 Well the book says "... the compiler will choose the move constructor to implement the transfer of the return value out of the function. This means that r=x+y+z will involve no copying of Vectors. Instead, Vectors are just moved" and its important for vectors with large amounts of data. (in this example the variables are vectors of same length and operator+() is defined for vector) Also how does your method refer to "this" instead of child_vector ? Apparently your way seems better but I'm having trouble implementing it - can you show me please :) – Amr Sep 23 '17 at 15:59
  • 1
    @hammeramr No, what I meant with `child_vector` is if you had a member called that in the class. But you don't, so forget that. Also, is there really no return-value-optimization, if you don't have overloaded move ctors/assignments? – Post Self Sep 23 '17 at 17:23
  • It seems so, in the example (see function below) he says that `z = x + y + z` will copy the return result twice "If a Vector is large, say, 10,000 doubles, that could be embarrassing." But "Given that definition, the compiler will choose the move constructor to implement the transfer of the return value..." He invented c++ so ill just take his word for it :). `Vector operator+(const Vector& a, const Vector& b) { if (a.size()!=b.size()) throw Vector_size_mismatch{}; Vector res(a.size()); for (int i=0; i!=a.size(); ++i) res[i]=a[i]+b[i]; return res; }` – Amr Sep 23 '17 at 19:18
  • 1
    See also http://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom – Jive Dadson Sep 23 '17 at 23:29

1 Answers1

3

Since this question was answered in the comments I thought I'd follow the advice from the meta: Question with no answers, but issue solved in the comments (or extended in chat) and write a short Community Wiki to close and answer the question.

I will also add useful additional info and tips from other users who joined the discussion in the comments.

Bo Presson answering and providing additional info on template placement:

The move assignment seems reasonable, except that putting templates in a cpp file makes them usable in that cpp file only. See Why can templates only be implemented in the header file?

Rakete1111 clarifying a misconception I had regarding move semantics:

std::move != move semantics. You have move semantics, where rvalues can be moved (using the move constructor) instead of copied. std::move is just a facility to enable move semantics (like using the move constructor) for types that are not rvalues.

kim366 bringing up return optimization question with Jive Dadson and I answering:

... Also, is there really no return-value-optimization, if you don't have overloaded move ctors/assignments ? -kim366

It seems so, in the example (see function below) he says that z = x + y + z will copy the return result twice "If a Vector is large, say, 10,000 doubles, that could be embarrassing." But "Given that definition, the compiler will choose the move constructor to implement the transfer of the return value..." He invented c++ so ill just take his word for it :). Vector operator+(const Vector& a, const Vector& b) { if (a.size()!=b.size()) throw Vector_size_mismatch{}; Vector res(a.size()); for (int i=0; i!=a.size(); ++i) res[i]=a[i]+b[i]; return res; } - hammeramr

(Example was from the book: "The C++ programing language 4th edition" by Bjarne Stroustrup)

See also What is the copy-and-swap idiom? -Jive Dadson

Hope people find this useful and thanks for those who participated.

Amr
  • 411
  • 6
  • 21