2

When a function doesn't modify an object argument, I always make it ask for a constant reference even if the referenced object isn't really constant. Is this wrong?

For a wrapper class, I'd like to write this:

template<class B>
class Wrapper{
private:
  B* base_;
public:
  Wrapper(const B& b) { base_ = const_cast<B*>(&b); }
  void ModifyBase();
};

The constructor doesn't modify the base so it asks for a constant reference.

The wrapper have some methods who will need to modify the base so it needs to store a non-constant pointer (thus the conversion).

I feel my solution is not the best.

Is there a better way to do this?

Is there any accepted convention?

dogiordano
  • 691
  • 6
  • 13
  • 1
    I guess it's wrong if you want to modify the argument. As I uderstand it, `const` in argument means "*I only need to see this value - I won'd do anything with it!*". – Tomáš Zato Jun 23 '14 at 16:58
  • 1
    `X const x; Wrapper w(x); w.ModifyBase();` and you have Undefined Behaviour. Do not strip away the const, unless you want *only* to mess with the type system (deprecated APIs, DRY). Do not strip away the const if you intend to modify the object. – dyp Jun 23 '14 at 16:58

2 Answers2

9

When you choose your parameter to be a const reference, you're telling the user "You can trust that if you pass me an object, it will not get modified [through this reference]†." You should do that as often as possible, because the user can understand more about what your function will and won't do just from looking at the types. Also, passing around mutable references can lead to code that is difficult to reason about.

However, in your question, your const is not telling the truth. It is casting away the constness and storing a non-const pointer - this means the object may very well get modified. You lied to the user! It doesn't matter that the constructor itself does nothing to do the object. It allows it to be modified by other member functions. This is bad behaviour. Your constructor should not take a const reference.

Not only that, but your current implementation allows undefined behaviour. Even if an object that is originally declared as const is given to your Wrapper, it doesn't care. It casts away it's constness and allows the other member functions to modify it. Modifying an object that was originally const is undefined behaviour.

† See 6502's comment

Joseph Mansfield
  • 108,238
  • 20
  • 242
  • 324
  • 1
    This is not entirely correct. The promise is "If you pass me an object I won't modify it **using this reference**"; it's perfectly fine to call e.g. a global function that knows that object and that mutates it. As another example if you pass the same object twice to a function accepting a const reference and a non-const reference the object can be mutated (using the second reference): passing a const reference doesn't imply a promise of not modifying an object. The "const reference" promise is only about that reference, tells nothing about the object. – 6502 Jun 23 '14 at 17:07
  • @6502 That is not correct: Passing the same object via const and non-const reference, the modification applied thorough a const_cast of the const reference is undefined behavior! –  Jun 23 '14 at 17:12
  • @DieterLücking: seems you missed the "(using the second reference)" part. Note that anyway you're wrong: casting away const-ness from a const reference and using it to modify an object is perfectly fine if the object is not constant (it's what `const_cast` was designed for). – 6502 Jun 23 '14 at 17:17
  • @6502 Have a look at: 7.1.6.1 (3/4) –  Jun 23 '14 at 17:18
  • @DieterLücking: that is talking about `const` objects. Const objects and const references are two very different concepts in C++. – 6502 Jun 23 '14 at 17:22
  • Thanks, that's what I wanted to know. And thanks to 6502 for the clarification. – dogiordano Jun 23 '14 at 17:45
1

It doesn't really matter that the ctor won't alter the object in the ctor, what happens after the ctor is done is why you need a non-const object pointer to B. So it has to do with ownership and lifetime of the B object passed in: if you want to take ownership (via the & reference, then the object must be non-const because it can be altered. If you want to simply copy the B object passed in, then don't use a refernce, pass by value and store a pointer to the copy.

Paul Evans
  • 27,315
  • 3
  • 37
  • 54
  • We usually talk about "owners" as being the parts of code that are responsible for destroying an object. So `&` is a transfer of ownership. – Joseph Mansfield Jun 23 '14 at 17:07