COMS W4995 C++ Deep Dive for C Programmers

Index of 2025-9/code/06

Parent directory
Makefile
intarray2.cpp
rvalue-test.cpp

Makefile

CC  = g++
CXX = g++

CFLAGS   = -g -Wall
CXXFLAGS = -g -Wall -std=c++14 -fno-elide-constructors

executables = intarray2 rvalue-test

.PHONY: default
default: $(executables)

intarray2: intarray2.o

rvalue-test: rvalue-test.o

.PHONY: clean
clean:
	rm -f *~ a.out core *.o $(executables)

.PHONY: all
all: clean default

intarray2.cpp

#include <string>
#include <iostream>

class IntArray {
public:
    IntArray() : sz{0}, cap{1}, a{new int[cap]} {}

    ~IntArray() {
        delete[] a;
    }

    IntArray(const IntArray&) = delete;
    IntArray& operator=(const IntArray&) = delete;

    // This is how we can explicitly request compiler-generated versions:
    // IntArray(const IntArray&) = default;
    // IntArray& operator=(const IntArray&) = default;

    /*
    // Move constructor
    IntArray(IntArray&& tmp) : sz{tmp.sz}, cap{tmp.cap}, a{tmp.a} {
        tmp.sz = tmp.cap = 0;
        tmp.a = nullptr;
        std::cout << "move ctor" << std::endl;
    }
    */

    /*
    // Move assignment
    IntArray& operator=(IntArray&& tmp) {
        if (this != &tmp) {
            delete[] a;

            sz = tmp.sz;
            cap = tmp.cap;
            a = tmp.a;

            tmp.sz = tmp.cap = 0;
            tmp.a = nullptr;
        }
        std::cout << "move assignment" << std::endl;
        return *this;
    }
    */

    int& operator[](int i) { return a[i]; }
    const int& operator[](int i) const { return a[i]; }

    size_t size() const { return sz; }
    size_t capacity() const { return cap; }

    void push_back(int x) {
        if (sz == cap) {
            // Separate cap*=2 to provide strong exception guarantee
            // int *a2 = new int[cap *= 2];
            int *a2 = new int[cap * 2];
            cap *= 2;
            std::copy(a, a+sz, a2);
            delete[] a;
            a = a2;
        }
        a[sz++] = x;
    }

private:
    size_t sz;
    size_t cap;
    int *a;
};

std::ostream& operator<<(std::ostream& os, const IntArray& ia) {
    for (size_t i = 0; i < ia.size(); i++) {
        os << ia[i] << " ";
    }
    std::cout << "(cap=" << ia.capacity() << ")" << std::flush;
    return os;
}

/*
IntArray createIntArray() {
    IntArray tmp;
    for (int i = 0; i < 20; i++) {
        tmp.push_back(i);
        std::cout << tmp << std::endl;
    }
    return tmp;
}
*/

int main() {
    using namespace std;

    // 1. Move construction

    IntArray ia { /* createIntArray() */ };

    cout << "ia: " << ia << endl;

    /*
    // 2. Move assignment

    IntArray ia2;

    ia2 = ia;
    //ia2 = (IntArray&&)ia;
    //ia2 = std::move(ia);

    cout << "ia2: " << ia2 << endl;
    cout << "ia: " << ia << endl;
    */
}

rvalue-test.cpp

#include <iostream>
using namespace std;

struct X {
    X() : d{100} {}

    double d;
};

void f1(X& t)       { t.d *= 2;  cout << t.d << endl; }

void f2(const X& t) { cout << "can't change t" << endl; }

void f3(X&& t)      { t.d *= 3;  cout << t.d << endl; }

int main() {
    X x;           // x is an lvalue
                  
    f1(x);         // passing an lvalue to X&       --> ok
    f2(x);         // passing an lvalue to const X& --> ok
    // f3(x);      // passing an lvalue to X&&      --> not ok

    // f1( X{} );  // passing an rvalue to X&       --> not ok
    f2( X{} );     // passing an rvalue to const X& --> ok
    f3( X{} );     // passing an rvalue to X&&      --> ok
}