Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

std::reverse doesn't work on a vector of objects with non default copy assignment operator

In the following example vec keeps the same order before and after calling std::reverse. If I make Foo.a not const and remove the copy assignment operator then the vector gets reversed properly, however I don’t understand why.

When debugging the assignment operator does seem to copy the object properly when called inside std::reverse so I don’t understand what I should be doing differently.

#include <vector>
#include <algorithm>
#include <iostream>

struct Foo
{
    Foo(int a, int b) : a(a), b(b) {}
    Foo operator=(const Foo& other)
    {
        return Foo(other);
    }
    const int a;
    int b;
    Foo doThing()
    {
        Foo copy(*this);
        copy.b++;
        return copy;
    }
};

void print(std::vector<Foo>& vec)
{
    for (const auto& foo : vec)
    {
        std::cout << foo.b << '\n';
    }
}

int main()
{
    Foo initial(-1, 0);
    std::vector<Foo> vec;
    vec.push_back(initial);
    for (size_t i = 0; i < 10; i++)
    {
        vec.push_back(vec[i].doThing());
    }
    std::cout << "before std::reverse \n\n";
    print(vec);
    std::reverse(vec.begin(), vec.end());
    std::cout << "\nafter std::reverse \n\n";
    print(vec);
    std::cout.flush();
}

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

>Solution :

This wasn’t possible prior to c++20 as const members could not be changed via assignment w/o UB and is the reason const members in objects were/are strongly discouraged. But now it is. But you still have to write a assignment operator. Here’s the working code and involves no UB:

#include <vector>
#include <algorithm>
#include <iostream>
#include <memory>

struct Foo
{
    Foo(int a, int b) : a(a), b(b) {}
    Foo& operator=(const Foo& other)
    {
        if (this == &other)
            return *this;
        std::destroy_at(this);
        std::construct_at(this, other);
        return *this;
    }
    const int a;
    int b;
    Foo doThing()
    {
        Foo copy(*this);
        copy.b++;
        return copy;
    }
};

void print(std::vector<Foo>& vec)
{
    for (const auto& foo : vec)
    {
        std::cout << foo.b << '\n';
    }
}

int main()
{
    Foo initial(-1, 0);
    std::vector<Foo> vec;
    vec.push_back(initial);
    for (size_t i = 0; i < 10; i++)
    {
        vec.push_back(vec[i].doThing());
    }
    std::cout << "before std::reverse \n\n";
    print(vec);
    std::reverse(vec.begin(), vec.end());
    std::cout << "\nafter std::reverse \n\n";
    print(vec);
    std::cout.flush();
}
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading