5

I have the following code

class a {
public:
    const int aa;
    a(int aa) : aa(aa){}
};
int main() {
    std::vector<a> v;
    v.emplace_back(1);
    v.emplace_back(2);
    v.emplace_back(3);
    v.emplace_back(4);

    std::iter_swap(v.begin() + 1, v.rbegin());

    system("pause");
    return 0;
}

I get an error when I try to swap two elements of the vector.

Error   C2280   'a &a::operator =(const a &)': attempting to reference a deleted function

I understand it's because a has a constant member, but I am not able to figure out how to get this to work.

MichaelMitchell
  • 1,069
  • 2
  • 13
  • 38

4 Answers4

11

The problem is that the swap changes the value of existing element. Since the elements are const, they cannot be changed hence the compiler error.

If you insist on const elements, your vector should be vector<a*> or event better vector<unique_ptr<a>>. This will allow you to swap elements without mutating the actual elements of a

doron
  • 27,972
  • 12
  • 65
  • 103
2

Based on this thread following works:

#include <vector>
#include <iostream>

class Foo {
public:
    const int value;
    Foo(const int &&from) : value(std::move(from)){}
    Foo(const Foo &&other) : value(std::move(other.value)){}
    Foo & operator=(Foo && source) {
        this -> ~ Foo ();
        new (this) Foo(std::move(source));
        return *this;
    }
};

int main() {
    std::vector<Foo> v;
    v.emplace_back(1);
    v.emplace_back(2);
    v.emplace_back(3);
    v.emplace_back(4);

    std::cout << (v.begin() + 1)->value << "," <<v.rbegin()->value << std::endl;
    std::iter_swap(v.begin() + 1, v.rbegin());
    std::cout << (v.begin() + 1)->value << "," <<v.rbegin()->value << std::endl;
    return 0;
}
Community
  • 1
  • 1
j2ko
  • 2,479
  • 1
  • 16
  • 29
  • If `Foo` is inherited from, or if any of the destroy/create operations could throw, this is not recoverable from... – Yakk - Adam Nevraumont Aug 31 '16 at 15:38
  • I'm not saying it is a solution, just one more option to discution. – j2ko Aug 31 '16 at 16:12
  • I'm just saying it is an insane solution. I cannot think why it isn't always illegal, so it is *a* solution. – Yakk - Adam Nevraumont Aug 31 '16 at 18:25
  • This not terrible at all. Foo cannot be inherited from since Foo is constructed inplace in the vector and placement new just calls the constructor, so if the constructor does not throw, the placement new will never throw. (normal new does a memory allocation that can fail) – doron Sep 01 '16 at 16:34
  • Took me a while to understand, but I figured it out. It was especially interesting to learn about placement new. Thanks! – MichaelMitchell Sep 04 '16 at 10:09
0

It is possible. Since std::iter_swap for std::vector iterators implementation looks like this:

using std::swap;
swap(*iter1, *iter2);

you can define your own swap function:

class A {
    const int aa;
public:
    A(int aa) : aa(aa){}

    void swap(A &other)
    {
        std::swap(const_cast<int &>(aa), const_cast<int &>(other.aa));
    }
};

void inline swap(A &a1, A &a2)
{
    a1.swap(a2);
}

And it works!

For more information about how this works see, for example, famous Scott Meyers book "Effective C++", rule 25. If you can't find, I can try to explain it (mr. Meyers does it better, I think).

@doron's tip about vector of pointers is a really good alternative when objects are large.

EDIT

@Yakk suggests that to cast a const variable to the non-const one is to cause undefined behaviour. It is better not to use this way.

ilotXXI
  • 1,075
  • 7
  • 9
0

It also works without r-value and std::move

const a& operator=(const a &other)
{
    this->~a();
    new (this) a(other.aa);
    return *this;
}
Artem Popov
  • 344
  • 2
  • 11