3

I have a struct like this:

struct OBJ {
  int x;
  const int y;
  OBJ& operator=(OBJ &&oth)
  {
    y = oth.y; // this is disallowed
    return *this;
  }
}

And an example code

void func() {
  static OBJ obj;
  OBJ other; // random values
  if(conditon)
    obj = std::move(other); //move
}

I understand this as obj is Non const OBJ with const member y. I can't change just y but I should be able to change whole object (call destructor and constructor). Is this possible or the only proper solution is to remove my const before y, and remember to don't change by accident?

I need to store my static obj between func call but if condition is true i want to move other object in place of this static object.

Cœur
  • 37,241
  • 25
  • 195
  • 267
S.R
  • 2,411
  • 1
  • 22
  • 33
  • [Off Topic] `obj = std::move(OBJ());` is not needed. `OBJ()` is already a rvalue so `obj = OBJ();` does the same thing. – NathanOliver Sep 08 '17 at 11:51
  • 1
    Use a [member initializer list](http://en.cppreference.com/w/cpp/language/initializer_list). – Some programmer dude Sep 08 '17 at 11:51
  • You will need to write your own move assignment operator (and while you're at it, the other special member functions) – AndyG Sep 08 '17 at 11:51
  • @NathanOliver this code is just example in real `func` this object allready exist and I want move it not construct! – S.R Sep 08 '17 at 11:52
  • And perhaps you should [read a good beginners book](http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list) as well, because you have a constructor, and those should not return anything. – Some programmer dude Sep 08 '17 at 11:52
  • @Someprogrammerdude I update question. I made mistake. I want assigment move, not constructor. – S.R Sep 08 '17 at 11:55

3 Answers3

2

You're doing constructors wrong. Constructors should initialize, not assign:

OBJ(OBJ &&oth) : y(oth.y) {}
//             ^^^^^^^^^^

Also, constructors cannot return *this, since they have no return type.

An assignment operator for your class doesn't make sense since the class has unassignable members (namely constants). (You could of course write a custom assignment that doesn't modify the const member, but then you'd have a truly weird class that has extremely surprising behaviour.)

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
2

What about writing the move-assignment operator this way:

OBJ& operator=(OBJ&& other) {
    this->~OBJ();
    new(this) OBJ(other.x, other.y);
    return *this;
}

You would also need a constructor:

OBJ(const int x, const int y)
    : x(x), y(y)
{
}
Miroslav Mares
  • 2,292
  • 3
  • 22
  • 27
  • Isn't it UB to placement-new over a non-dynamic object with const members?! – Kerrek SB Sep 08 '17 at 11:57
  • 1
    Well, this solution is by large part inspired by this answer: https://stackoverflow.com/a/39382728/1003701 The question is about `std::launder` and it seems that launder is the way to fully employ technique mentioned in my answer without getting into Undefined Behaviour lands. See also: http://en.cppreference.com/w/cpp/utility/launder – Miroslav Mares Sep 08 '17 at 12:04
  • @MiroslavMares Nice, but is this safe when I have no trivial member in my struct? http://en.cppreference.com/w/cpp/utility/launder looks great but it's c++17 I need C++14 / C+11 solution. – S.R Sep 08 '17 at 12:15
  • @S.R Is it thinkable to modify the struct definition to remove the "`const`"? If so, removing it and enforcing constness of the `y` member variable by making it private and setting up getters/setters might be an attractive possibility. – Miroslav Mares Sep 08 '17 at 12:30
  • 1
    @S.R - This is terribly unsafe if the "reconstruction" can possibly fail with an exception. Then you have a zombie object - an object that is alive but dead. – Bo Persson Sep 08 '17 at 12:37
  • @BoPersson Good point, one could end with a static variable, which was destructed but constructor did throw, leaving it in an uninitialized state. – Miroslav Mares Sep 08 '17 at 12:40
  • @S.R Worse than what Bo described, unless the constructor is noexcept, you can (and will) potentially destroy the same object (the original `this`) *twice*. Scope has no clue you manually already fired the destructor, so throwing out of this function on an exception from the constructor invoked via placement-new is a huge problem. – WhozCraig Sep 08 '17 at 12:41
  • 1
    For now I removed `const`, but I don't like this code. This variable should be const. It's not changing but moving. (Old is destructed, new just changing place in memory, so logically this should work in my opinion). I am waiting for std::launder. – S.R Sep 08 '17 at 13:53
2

I would suggest moving to std::unique_ptr:

void func() {
  static std::unique_ptr<OBJ> obj = std::make_unique<OBJ>();
  std::unique_ptr<OBJ> other = std::make_unique<OBJ>(); // random values
  if(condition)
    obj = std::move(other); //move
}

This should be your choice in many cases where there is a need to move something that cannot be moved, to hold an unknown polymorphic type or any other case where you cannot deal with the actual type.

Amir Kirsh
  • 12,564
  • 41
  • 74
  • This will work with const values? It look like it do the work! Excellent solution! – S.R Sep 11 '17 at 11:20
  • @S.R it should work. well, you can see it does. I would advise, objectively as I can, to mark it as the accepted answer :-) – Amir Kirsh Sep 11 '17 at 12:40
  • @amir-krish Yes I will accept it as soon as I test it. I have another question Can I tell compiler to accept my struct only as uniq_ptr? So the only way is generate uniq pointer. I wonder about private constructors and make_obj function as a friend. – S.R Sep 11 '17 at 13:05
  • 1
    @S.R by the master of RValues himself: https://stackoverflow.com/a/17135547/2085626 another option can be found here: http://seanmiddleditch.com/enabling-make_unique-with-private-constructors/ – Amir Kirsh Sep 11 '17 at 13:43