8

I want to implement a Swap() method for my class (let's call it A) to make copy-and-swap operator=(). As far as I know, swap method should be implemented by swapping all members of the class, for example:

class A 
{
  public:
    void swap(A& rhv) 
    {
        std::swap(x, rhv.x);
        std::swap(y, rhv.y);
        std::swap(z, rhv.z);
    }
  private:
    int x,y,z;
};

But what should I do if I have a const member? I can't call std::swap for it, so I can't code A::Swap().

EDIT: Actually my class is little bit more complicated. I want to Serialize and Deserialize it. Const member is a piece of data that won't change (its ID for example) within this object. So I was thinking of writing something like:

class A
{
  public:
    void Serialize(FILE* file) const
    {
        fwrite(&read_a, 1, sizeof(read_a), file);
    }

    void Deserialize(FILE* file) const
    {
        size_t read_a;
        fread(&read_a, 1, sizeof(read_a), file);
        A tmp(read_a);
        this->Swap(tmp);
    }

 private:
   const size_t a;
};

and call this code:

A a;
FILE* f = fopen(...);
a.Deserialize(f);

I'm sorry for such vague wording.

f0b0s
  • 2,978
  • 26
  • 30
  • Neil Butterworth: i dont want to logically change it, a want to physically swap ALL members of class. They wont notice it) – f0b0s Feb 05 '10 at 21:09
  • 1
    Help me out please. What would operator=() do if there were a const member in the class? I mean what is the intention? – Notinlist Feb 05 '10 at 21:15
  • @f0b0s: If you give us the definition of your class you're trying to write swap for, we'll tell you how to get the results you desire. But a class with a const data member is a class not meant to be changed. – GManNickG Feb 05 '10 at 21:20
  • I agree with GMan, if it has a const data member, you should be copying the class instead. – DigitalZebra Feb 05 '10 at 21:21
  • 1
    It might help if we understood the larger context. What is it you are really trying to achieve? What is `class A`, really? – John Dibling Feb 05 '10 at 21:22
  • 1
    @John (+1) @Neil If the object of person A (acct-id const) represent by mem address 1 is going to be swapped by person B (acct-id const) represented by memory address 2. Then I see no harm in swapping the object -- including the acct-id... the swap is meant to ... (wait for it.....) SWAP ! it doesn't mean to change the value... C++'s const correctness is for a fairly restricted set of use-cases. – Hassan Syed Feb 05 '10 at 21:35
  • Hassan Syed: thank you, you exactly explained my problem. – f0b0s Feb 05 '10 at 21:41
  • f0b0s: So is `class A` kind of like a document object you are trying to load and save? – John Dibling Feb 05 '10 at 21:48
  • @Hassan: In that case, then why does the class have `const` members? – John Dibling Feb 05 '10 at 21:49
  • @John Dibling mm... a person have a const color of skin for example, or const sex. – f0b0s Feb 05 '10 at 21:53
  • John Dibling: well, it is a container with std::list of data of constant size. – f0b0s Feb 05 '10 at 21:54
  • 3
    @f0b0s: Ok, fair enough. But your'e not going to swap that person for another. The point of this is that when you have a class with const member variables, and a swap method, something just isn't right. Either the members shouldn't be const or you shouldn't be swapping. Generally speaking. – John Dibling Feb 05 '10 at 21:56
  • @f0b0s, but if you intend on swapping them (making your class mutable), then that data shouldn't be constant. Also, is the std::list itself constant? – DigitalZebra Feb 05 '10 at 21:57
  • @f0b0s: And why do you want to swap lists? Not trying to hassle you here. Just trying to get to the bottom of what you're trying to accomplish. – John Dibling Feb 05 '10 at 21:57
  • 1
    Your code still seem to be missing something. And I still can't see why you want to construct a temporary and swap it. (At least, are you sure you want a public swap method. May-be you just want a private helper of some kind, which would just swap the things you want swapped?) – UncleBens Feb 05 '10 at 22:00
  • I saw your edit, but what didn't make sense to me is why the size member is const? Lets assume that it does make sense to swap lists. In that case, the size member should not be const, because the size of the contained list CAN change. – John Dibling Feb 05 '10 at 22:03
  • @UncleBens Yes, i've implemented SwapNonConst, but i don't like this realization. @John Dibling now, this const size is size of each element in non-const list. – f0b0s Feb 05 '10 at 22:07
  • 1
    @f0b0s: Ah, I see. Shouldn't that be a property of the element, and not the list? – John Dibling Feb 05 '10 at 22:09
  • 1
    @f0b0s: Or alternatively, if the size of the element is TRULY immutable, as in it wouldn not even change accross different instantiations of the list, then should it not be a static const member of the list? (I still prefer attribute of the element, however) – John Dibling Feb 05 '10 at 22:12
  • @John Dibling may be you right, both versions are really good, thanks! – f0b0s Feb 05 '10 at 22:14
  • 1
    @John Dibling const is a fantastic qualifier -- when it has it captures the requirements of the problem at hand; solving problems is what we do with programming languages. Sometimes the problem gets in the way of writing elegant code, and in that case we need to make some concessions. This could well be one of those cases -- the values are indeed const -- however the memory locations, may at times in the code, not be -- perhaps the OP should use T * const x ?. I've added this as an answer. – Hassan Syed Feb 06 '10 at 12:10

7 Answers7

10

I think what you really want is to have an internal data structure that you can easily exchange between objects. For example:

class A 
{
   private:

     struct A_Data {
       int x;
       int y;
       const int z;

       A_Data(int initial_z) : z(initial_z) {}
    };

    std::auto_ptr<A_Data> p_data;

  public:

     A(int initial_z) : p_data(new A_Data(initial_z)) {}

     void swap(A& rhv) {
        std::swap(p_data, rhv.p_data);
     }
};

This keeps the z value constant within any instance of A object internal data, but you can swap the internal data of two A objects (including the constant z value) without violating const-correctness.

Tyler McHenry
  • 74,820
  • 18
  • 121
  • 166
  • 1
    Yeah, except now A's copy constructor behavior is questionable at best and its assignment operator is flat-out broken. So don't forget to write a correct copy constructor and assignment operator if you go this route. – Jon-Eric Feb 05 '10 at 21:27
  • Well, yes, you still do have to follow the general rule of "if you need any of: Destructor, Copy Constructor, Assignment Operator, then you need all three." (and swap is a sort of assignment operator). The code above is intended to show the idea behind the const-correct swap, not be a complete implementation. – Tyler McHenry Feb 05 '10 at 21:28
  • This make `p_data->z` not modifiable but you can change the whole `A_Data` object. Normally if you want a const member is to assure that nobody change it... – Isaac Pascual Jan 29 '20 at 15:17
5

After a good nights sleep I think the best answer is to use a non-const pointer to a const value -- after all these are the semantics you are trying to capture.

Alexander
  • 23,432
  • 11
  • 63
  • 73
Hassan Syed
  • 20,075
  • 11
  • 87
  • 171
  • thank you, you are the only one, who understood the problem: I don't want to change VALUE, but change the ADDRESS (location) of const variable. I've done this using poiter it's the most elegant I think. – f0b0s Feb 06 '10 at 12:33
3

f0b0s, a good design principle is to design your objects to be immutable. This means that the object can't change once created. To "change" the object, you must copy the object and make sure to change the elements you want.

That being said, in this case you should look at using a copy constructor instead to copy the objects you want to swap, and then actually swap the references to the object. I can understand it'd be tempting just to be able to change the elements of an object under the hood, but it'd be better to make a copy of the object and replace the references to that object with the NEW object instead. This gets you around any const nastiness.

Hope this helps.

DigitalZebra
  • 39,494
  • 39
  • 114
  • 146
1

I suggest you use pointers to the instances. The pointers can be swapped much easier than the data in the class or struct.

The only way to swap a constant value is to create another object, or clone the current object.

Given a struct:

struct My_Struct
{
  const unsigned int ID;
  std::string        name;
  My_Struct(unsigned int new_id)
    : ID(new_id)
  { ; }
};

My understanding is that you want to swap instances of something like My_Struct above. You can copy the mutable (non-const) members but not the const member. The only method to alter the const member is to create a new instance with a new value for the const member.

Perhaps you need to rethink your design.

Thomas Matthews
  • 56,849
  • 17
  • 98
  • 154
0

This is why const_cast was created. Just remember not to shoot your foot off.

Edit: OK, I concede - const_cast wasn't made for this problem at all. This might work with your compiler, but you can't count on it and if demons come flying out of your nostrils, please don't blame me.

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • 6
    This is definitely not the reason that `const_cast` was created. If the member is genuinely `const` then using `const_cast` to enable writing to that member will cause undefined behaviour. `const_cast` only enables writing to something that isn't `const` but where the writing code happens to only have a pointer to `const` or a `const` reference. – CB Bailey Feb 05 '10 at 21:11
  • 1
    I don't think it's a good idea. As far as I know, const_cast on genuinely const object causes undefined behavior. – f0b0s Feb 05 '10 at 21:13
  • 1
    (+1) const_cast is used when the semantics of the language doesn't match the semantics of the problem at hand.... – Hassan Syed Feb 05 '10 at 21:39
  • 1
    Eh, sorta. Put another way, const_cast is used when somebody screws up and you have to hack around it. – John Dibling Feb 05 '10 at 22:06
0

IMHO you must consider not to swap CONST members.

PD: I think you could consider to use reflection in your approach. so you don't have to maintain the function.

SDReyes
  • 9,798
  • 16
  • 53
  • 92
0

tl;dr; : It's Undefined Behavior.

Reference/reason: CppCon 2017: Scott Schurr “Type Punning in C++17: Avoiding Pun-defined Behavior, @24m52s +- ”

My interpretation, by example:

Suppose you create an object of type T, which have some const members. You can pass this object as a non-const reference to a function f(&T) that manipulates it, but you'd expect the const members to remain unalterable after the call. swap can be called in non-const references, and it can happen inside the function f, breaking the premise of const members to the caller.

Every part of your code that uses swap would have to assert that the object of type T being swapped does not belong to any context where the const members are assumed constant. That is impossible to automatically verify*.

*I just assumed that this is impossible to verify because it seems like an extension of the undecidability of the halting problem.

Kahler
  • 1,130
  • 6
  • 24