user2313793
user2313793

Reputation:

will move semantics prevent copying here?

// --- Move constructor
Matrix(Matrix&& other) throw() : data_(other.data_), Rows_(other.Rows_), Columns_(other.Columns_) { other.Rows_ = other.Columns_ = 0; other.data_ = nullptr; }

// --- Move assignment
Matrix & operator=(Matrix&& other) throw() {
    using std::swap;
    swap(Rows_, other.Rows_);
    swap(Columns_, other.Columns_);
    swap(data_, other.data_); 
    return *this; }

MultiplyAdd implementation:

template <class T>
Matrix<T> MultiplyAdd(const T a,const Matrix<T> &x,const T b) {
    Matrix<T> out(a*x+b);
    return out; }

template <class T>
Matrix<T> MultiplyAdd(const Matrix<T> a,const Matrix<T> &x,const T b) {
    Matrix<T> out(a*x+b);
    return out; }

int main(){
    Matrix<> x = // some initialization
    auto&& temp_auto = MultiplyAdd(a,x,b);
    for (int i = 1; i < N-1; i++) {
        temp_auto = MultiplyAdd(temp_auto,temp2,b); }
    return 0;
}

Question: Will the use of the auto keyword in the last code snippet avoid the creation of temporaries? Before and more important inside the 'for' loop.

Upvotes: 2

Views: 193

Answers (1)

Andy Prowl
Andy Prowl

Reputation: 126582

Will the use of the auto keyword in the last code snippet avoid the creation of temporaries?

No. A temporary needs to be created anyway, since temp_auto is a reference, and there must be something that reference is bound to.

Your odds to avoid the creation of a temporary would be higher if you had done:

auto temp_auto = MultiplyAdd(a,x,b);

In which case the compiler could perform copy/move elision and construct the result of MultiplyAdd() directly into temp_auto, without having to call the move constructor.

The reason I am talking about "odds" is that per paragraph 12.8/31 of the C++11 Standard, the compiler is entitled, but not obliged, to perform copy/move elision.

To clarify what is going on, I will try to explain what the compiler has to do when returning an object. Consider this simple function and the subsequent function call:

X foo() { X x; /* ... */ return x; }

// ...

X y = foo();

Here, when returning x, the compiler would have to:

  1. Move-construct a temporary from x (let's call it t);
  2. Move-construct y from t.

Now thanks to copy elision, the compiler is allowed to avoid the creation of the temporary t, construct the returned object x directly in y, and elide both calls to the move constructor.

On the other hand, inside the loop:

temp_auto = MultiplyAdd(temp_auto,temp2,b);

You are doing an assignment. In our simple example, this is the equivalent of:

X foo() { X x; /* ... */ return x; }

// ...

X y;
y = foo();

Even here, when returning x from foo(), the compiler has to:

  1. Move-construct a temporary from x from foo() (let's call it t again);
  2. Move-assign t to y.

Even in this case, the creation of a temporary can be avoided by passing directly x (instead of t) to the move-assignment operator that assigns to y, although the call to the move-assignment operator cannot be elided (only calls to copy/move constructors can be elided).

Notice, that this is true both in your original example (where temp_auto is a reference) and in my modified example above, where temp_auto is an object of class type.

Upvotes: 1

Related Questions