Home > Back-end >  std::reverse doesn't work on a vector of objects with non default copy assignment operator
std::reverse doesn't work on a vector of objects with non default copy assignment operator

Time:01-16

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();
}

CodePudding user response:

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 an assignment operator. To future proof the code (past c 23) you should also include a copy ctor as the implicit copy ctor may be removed. 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;
    }
    Foo(const Foo& a) = default;  // Future proof
    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();
}
  •  Tags:  
  • c
  • Related