I'm trying to test my copy con and move con as well as their assignment operators. It NEVER gets invoked :S Copy con gets invoked just fine but the assignment operators are never called and neither is the move constructor. How can I test it to make sure it works just right? I've read a TON of tutorials on it to make sure I got everything down to the very last detail but now when it's time to test it, only the copy con is invoked. It doesn't actually load or save a bitmap. It's just a class I called that and put a vector member of a struct so I don't have to use pointers for now.. (Defined as vector<BYTE> Pixels). No implementation, just bare testing.

I just don't want under the hood problems or future problems. It's just a class I created specifically for learning Copy/Move/Swap semantics but I never know if I'll need it.

I'm testing with:

    Bitmaps A = Bitmaps("C:/Meh.bmp");
    Bitmaps B(Bitmaps("C:/Meh.bmp"));
    Bitmaps C = A;

    //I'll write code to test if change on one data member affects that of another object later.

And implementing with:

//Just a huge initiaization list. Copy Constructor:
Bitmaps::Bitmaps(const Bitmaps& Bmp) : Pixels(Bmp.Pixels), Info(Bmp.Info), width(Bmp.width), height(Bmp.height), size(Bmp.size), bFileHeader(Bmp.bFileHeader), TransparentColor(Rgb(0)), DC(0), Image(0){std::cout<<"Copy Constructor Called.\n"}

//Just a huge initialization list. Move Constructor:
Bitmaps::Bitmaps(Bitmaps&& Bmp) : Pixels(0), Info(), width(Bmp.width), height(Bmp.height), size(Bmp.size), bFileHeader(), TransparentColor(Rgb(0)), DC(0), Image(0)
{
    std::cout<<"Move Constructor Called.\n"
    this->Swap(Bmp);
    Bmp.Pixels.clear();  //A vector.
}

Bitmaps& Bitmaps::operator = (Bitmaps Bmp) //Pass by value.
{
    std::cout<<"Copy Assignment Called.\n"
    Bmp.Swap(*this);
    return *this;
}

Bitmaps& Bitmaps::operator = (Bitmaps&& Bmp)
{
    std::cout<<"Move Assignment Called.\n"
    Bmp.Swap(*this);
    return *this;
}

void Bitmaps::Swap(Bitmaps& Bmp) //throw()
{
    std::swap(Pixels, Bmp.Pixels);  //Vector.
    std::swap(Info, Bmp.Info);    //BmpInfoheader.
    std::swap(width, Bmp.width);
    std::swap(height, Bmp.height);
    std::swap(size, Bmp.size);
    std::swap(bFileHeader, Bmp.bFileHeader);  //BmpFileHeader.
    std::swap(Image, Bmp.Image);           //HBitmap.
    std::swap(DC, Bmp.DC);        //HDC.
}

The only thing that ever prints is: "Copy Contructor called.". Not even the assignment operator is called. I even tried making it RValue by doing operator = (const Bitmaps& BMP)

Is there some magic happening that I cannot see? Or am I just testing it or implementing it wrong? I read that RVO can cause such behaviour but I have my doubts in my implementation since it's my first time actually trying and testing my copy/move/swap stuff.

Edited 4 Years Ago by triumphost

The problem is you test program, the following three lines:

Bitmaps A = Bitmaps("C:/Meh.bmp");
Bitmaps B(Bitmaps("C:/Meh.bmp"));
Bitmaps C = A;

The first line will call the constructor that takes a string (or char pointer). That is simply the syntax to call an explicit constructor. The second line does the same thing as the first. And the third line is one way to construct an object using the copy-constructor.

If you want to invoke the copy-assignment operator, you should do:

Bitmaps D;  // default-construct it (or construct it otherwise, doesn't matter).
D = A;      //  then, you do the assignment.

If you want to invoke the move-constructor, you should do either of these things:

Bitmaps E(std::move(A));   // the std::move marks A as "to-be-moved".
Bitmaps F = std::move(A);

If you want to invoke the move-assignment operator, you should do this:

Bitmaps G;
G = std::move(A);
// Or, you can also do:
G = Bitmaps("C:/Meh.bmp");  // assign a temporary (rvalue).

And, by the way, if you have a copy-and-swap assignment operator which passes the object by value (which is correct), then you don't need a separate move-assignment operator because the copy-and-swap will also act as a move-and-swap if the passed value-object is constructed from the move-constructor (instead of the copy-constructor used in the copy-and-swap).

+1 for that tutorial! + another 1 for removal of the move assignment operator. Bookmarked your tutorial and the post above since no other sites mention this information. I was wondering why they looked the same but also why it said ambiguous overload since the tutorials I was reading said I needed both! One for copying and one for moving.

Now supposed it was a class as simple as in my example. Maybe even the exact same test class. What if it didn't implement copy con or move con and the compiler generated it, would I still use copy/swap idiom? Or would I just do the assignment operator the same as in:

Type& operator = (const Type& T)
{
    if (this != &T)
    {
        this->SomeMember = T.SomeMember;
    }
    return *this;
}

OR would I do the copy-swap? The next thing I read is that I should not (should not have to) implement copy-swap or copy/move constructors for POD types and vector based classes where the underlying type is just simple. Is this true? Does that mean my test class is bad since I used a vector and did the swap on it? Oh and my final question before I feel safe doing it on my own is: Why does your tutorial and some of the other have throw() next to the swap while other tutorials don't? I read on stackoverflow that it is not necessary and neither is noexcept :S

Edited 4 Years Ago by triumphost

What if it didn't implement copy con or move con and the compiler generated it, would I still use copy/swap idiom?

The compiler will also generate the copy- and move-assignment operators for you. However, if you have data members like a vector, you would still benefit from using a copy-and-swap implementation.

There is little point in using the copy-assignment operator that you have posted, because that is basically the same as what the compiler will generate for you. So, usually, if the compiler-generated copy- and move-constructors are OK, then the compiler-generated copy- and move-assignment operator are also OK.

The next thing I read is that I should not (should not have to) implement copy-swap or copy/move constructors for POD types and vector based classes where the underlying type is just simple. Is this true?

Yes. For POD-types, there is no point in creating your own copy- or move- functions (construct and assignment), because the compiler-generated versions are fine.

Does that mean my test class is bad since I used a vector and did the swap on it?

No. If you have something like a container (e.g., vector) which can benefit (performance-wise) from the use of a swap function, then you can benefit from defining your own swap and copy-and-swap functions. However, with the new standard and its move-semantics, the default swap function is pretty good already (because it relies on the move-assignment operators), so the need to implement your own swap is reduced.

Why does your tutorial and some of the other have throw() next to the swap while other tutorials don't?

The throw() or noexcept (C++11) is not really a big necessity, it is just part of your interface, telling users of your class that this function will never throw an exception. This is almost always the case with move-functions and swap-functions because they are usually very simple (no allocation of resources or anything like that). It is not strictly necessary (and doesn't do much), but it doesn't hurt (and also, the standard classes have noexcept specifications on the appropriate functions).

However, do not confuse the throw() or noexcept with the broader topic of exception specification, where you would do something like throw(std::range_error, std::bad_alloc), i.e., basically listing the possible exceptions that a given function could throw. This is bad, it's a stupid feature that is pretty much useless (it is ill-designed), and you should use that. You should only specify exceptions for the case that there are no exceptions that can be thrown, as in throw() or noexcept.

This question has already been answered. Start a new discussion instead.