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?
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
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.
Related
Sometimes I need to pass the reference of an object. I know the benefits of const correctness in order to avoid modifications in an object and to avoid a mess, but sometimes it's really hard to use it. If I pass an object to a method that is const correct and I call another method that doesn't accept any argument and don't modify the object itself and returns nothing it complains that I need to put it as const as well. And then I'm seeing my whole application getting "const" on the end of the methods even when it returns nothing or get nothing as arguments.
void doNothing() const {}
I understand that the compiler needs to make sure that any method isn't going to modify the object but this seems to be silly.
My question is: Should I always use const correct or there's a time that I can avoid using and pass an object as reference without being concern of what can happen?
And if has a theory behind it, please explain.
For freestanding functions, you should mark reference and pointer parameters as const if the function doesn't modify the parameters. You don't need to do anything for parameters that are passed by value.
For non-static class methods, there's also an implicit this parameter. You should mark the method itself as const if it doesn't modify this. Doing so allows you to call that method on const objects. If you don't mark a method as const then you can only call it on non-const objects.
class Foo
{
int foo;
public:
void doNothing() const
{
foo = 42; // not allowed: `this` is const
}
};
The hidden this parameter is why it matters, even when a method takes no other parameters. It's the hidden parameter that you're protecting.
If something does not modify your object make it const. One simple reason to do it: You cannot call a non-const member function on a const object. That is obviously because non-const member function are a allowed to change their instance, while a const instance cannot be changed. So making a function non-const without need produces logically flawed code: Why, for example, should it be illegal to print a const Matrix? That just does not make sense, so a print function for a matrix should be const.
This applies to free functions too: Temporaries can only bind to const references, not to non-const references. So the former is just more general.
And last but not least: The only drawback you mention is writing const over and over. So I ask you: How long does it take you to type const? Surely not long enough to accept a design flaw in your program.
Implementation 1:
foo(const Bar x);
Implementation 2:
foo(const Bar & x);
If the object will not be changed within the function, why would you ever copy it(implementation 1).
Will this be automatically optimized by the compiler?
Summary: Even though the object is declared as const in the function declaration, it is still possible that the object be edited via some other alias &.
If you are the person writing the library and know that your functions don't do that or that the object is big enough to justify the dereferencing cost on every operation, than
foo(const Bar & x); is the way to go.
Part 2:
Will this be automatically optimized by the compiler?
Since we established that they are not always equivalent, and the conditions for equivalence is non-trivial, it would generally be very hard for the compiler to ensure them, so almost certainly no
you ask,
“If the object will not be changed within the function, why would you ever copy it(implementation 1).”
well there are some bizarre situations where an object passed by reference might be changed by other code, e.g.
namespace g { int x = 666; }
void bar( int ) { g::x = 0; }
int foo( int const& a ) { assert( a != 0 ); bar( a ); return 1000/a; } // Oops
int main() { foo( g::x ); }
this has never happened to me though, since the mid 1990s.
so, this aliasing is a theoretical problem for the single argument of that type.
with two arguments of the same type it gets more of a real possibility. for example, an assignment operator might get passed the object that it's called on. when the argument is passed by value (as in the minimal form of the swap idiom) it's no problem, but if not then self-assignment generally needs to be avoided.
you further ask,
“Will this be automatically optimized by the compiler?”
no, not in general, for the above mentioned reason
the compiler can generally not guarantee that there will be no aliasing for a reference argument (one exception, though, is where the machine code of a call is inlined)
however, on the third hand, the language could conceivably have supported the compiler in this, e.g. by providing the programmer with a way to explicitly accept any such optimization, like, a way to say ”this code is safe to optimize by replacing pass by value with pass by reference, go ahead as you please, compiler”
Indeed, in those circumstances you would normally use method 2.
Typically, you would only use method 1 if the object is tiny, so that it's cheaper to copy it once than to pay to access it repeatedly through a reference (which also incurs a cost). In TC++PL, Stroustrup develops a complex number class and passes it around by value for exactly this reason.
It may be optimized in some circumstances, but there are plenty of things that can prevent it. The compiler can't avoid the copy if:
the copy constructor or destructor has side effects and the argument passed is not a temporary.
you take the address of x, or a reference to it, and pass it to some code that might be able to compare it against the address of the original.
the object might change while foo is running, for example because foo calls some other function that changes it. I'm not sure whether this is something you mean to rule out by saying "the object will not be changed within the function", but if not then it's in play.
You'd copy it if any of those things matters to your program:
if you want the side effects of copying, take a copy
if you want "your" object to have a different address from the user-supplied argument, take a copy
if you don't want to see changes made to the original during the running of your function, take a copy
You'd also copy it if you think a copy would be more efficient, which is generally assumed to be the case for "small" types like int. Iterators and predicates in standard algorithms are also taken by value.
Finally, if your code plans to copy the object anyway (including by assigning to an existing object) then a reasonable idiom is to take the copy as the parameter in the first place. Then move/swap from your parameter.
What if the object is changed from elsewhere?
void f(const SomeType& s);
void g(const SomeType s);
int main() {
SomeType s;
std::thread([&](){ /* s is non-const here, and we can modify it */}
// we get a const reference to the object which we see as const,
// but others might not. So they can modify it.
f(s);
// we get a const *copy* of the object,
// so what anyone else might do to the original doesn't matter
g(s);
}
What if the object is const, but has mutable members? Then you can still modify the object, and so it's very important whether you have a copy or a reference to the original.
What if the object contains a pointer to another object? If s is const, the pointer will be const, but what it points to is not affected by the constness of s. But creating a copy will (hopefully) give us a deep copy, so we get our own (const) object with a separate (const) pointer pointing to a separate (non-const) object.
There are a number of cases where a const copy is different than a const reference.
class A
{
...
public:
shared_ptr<Logger> GimmeLogger () const
{
return m_logger;
}
private:
shared_ptr<Logger> m_logger;
};
In class A, should GimmeLogger be const or non-const?
It would make sense to be const because it is a simple getter that doesn't modify *this (syntactic const).
But on the other hand, it returns a non-const pointer to another object that it owns (semantically non-const).
If you make that non-const, then you cannot write this:
void f(const A & a)
{
auto v = a.GimmeLogger(); //error
}
So if you want to write this; that is, if you want to call GimmeLogger on const object, then make GimmeLogger a const member function, because you cannot invoke a non-const member function, on const object. However, you can invoke a const member function, on non-const object (as well as on const object).
Inside a const member function, every member is semantically const objects. So the type of m_logger in the function becomes const share_ptr<const m_logger>. So change the return type accordingly.
Because const is a keyword, it is checked syntactically, but it should be used semantically, that is, in your design operations that don't change the visible state of your class should be marked as const.
That is the whole idea behind the mutable keyword: adding the ability to mark a member as this does not take part of the visible state of the object so that the syntactic check matches the semantic meaning. In your particular case, because you are copying a pointer, you don't even need to use mutable there (this is one of the weak points of const-correctness actually, as returning a non-const pointer does not trigger errors while compiling, even though you are opening a door for changes in your object)
In this particular case, on the other hand, I don't see a good reason by which the object would publicize it's logger... That is, const-correctness aside, why do you need to grant access to the logger?
Yes, it should be const. The const-ness of the function has nothing to do with the const-ness of the return type.
I get your point, but I think the function remains const either way.
Generally you shouldn't return a handle to a member data when you can avoid it. Try hard to review your design and find a way around this. That said, if you must, it should be const. This allows you to call the function on const objects as well as non-const objects. See for example std::string::c_str(). You can also overload the function so you get both, like standard containers do with iterators.
When in doubt, look in the standard library for a hint.
I need to write a class whose constructor takes a constant reference to a object and stores it locally.
In order to avoid most common mistakes I can foresee, I'd like to only accept references to non-temporary (ie: references to lvalues).
How can I write a function that takes constant references to non-temporary only?
Of course even a non-temporary could go out of scope and thus break my class behavior, but I believe that by disallowing temporary references I will avoid most mistakes.
If you are going to store a reference and need to use it after the constructor has completed, it's probably best for the constructor to take a pointer:
struct C {
C(const X* p) : p_(p) { }
const X* p_;
};
This way, it's pretty much guaranteed that you won't have a pointer to a temporary (unless X does something really dumb, like overloading the unary & to return this).
If the constructor takes a pointer, it's also clearer to users of the class that they need to pay attention to the lifetime of the X object they pass to the constructor.
What's better as default, to return a copy (1) or a reference (2) from a getter function?
class foo {
public:
std::string str () { // (1)
return str_;
}
const std::string& str () { // (2)
return str_;
}
private:
std::string str_;
};
I know 2) could be faster but don't have to due to (N)RVO. 1) is safer concerning dangling references but the object will probably outlife or the reference is never stored.
What's your default when you write a class and don't know (yet) whether performance and lifetime issues matter?
Additional question: Does the game change when the member is not a plain string but rather a vector?
Well it really depends on what you expect the behaviour to be, by default.
Do you expect the caller to see changes made to str_ unbeknownst(what a word!) to them? Then you need to pass back a reference. Might be good if you can have a refcounted data member and return that.
If you expect the caller to get a copy, do 1).
My rule of thumb is to return a copy for simple basic datatypes such as int, string etc. For a bit more complicated structures where copying may be costlier (like vector you mentioned) I prefer to return a const-reference.
The compiler will not be able to perform (N)RVO in this case. The (named) return value optimization is an optimization where the compiler creates the function auto variables in the place of the return value to avoid having to copy:
std::string f()
{
std::string result;
//...
return result;
}
When the compiler sees the code above (and assuming that if any other return is present it will also return the result variable) it knows that the variable result has as only possible fate being copied over the returned temporary and then destroyed. The compiler can then remove the result variable altogether and use the return temporary as the only variable. I insist: the compiler does not remove the return temporary, it removes the local function variable. The return temporary is required to fulfill the compilers call convention.
When you are returning a member of your class, the member must exist, and the call convention requires the returned object to be in a particular location (stack address usually). The compiler cannot create the method attribute over the returned object location, nor can it elide making the copy.
I'm returning a reference, because a string seems not "cheap to copy" to me. It's a complex data type with dynamic memory management and all that.
The "if you want the caller to get a copy, you should return by value" argument is moot, because it doesn't preclude copies at all. The caller can still do the following and get a copy anyway
string s = obj.str();
You need to explicitly create a reference on the caller side to be able to refer to the data member directly afterwards - but why would you do that? There definitely are enough user defined types that are cheap to copy
Smart Pointers
Iterators
All of the non-class types.
Returning a reference to an object's internals as part of its public interface can be a code smell if not outright bad design.
Before returning a reference to an internal object in a public interface, the designer should pause. Doing so couples users of your class to part of your design. Often it is outright unnecessary, sometimes it indicates further design work is needed. At times it is necessary, as commenters have noted.
If there is no special reason to use a value type as return value, I always return a const reference. If I need (or expect to need) a (writable) copy, I add a copy ctor and an assignment operator to the returned class if not already available. For the usage think of:
const MyClass & ref = container.GetAt( 1234 ); // need only reference
MyClass copy = container.GetAt( 1234 ); // get writable copy
Actually this is quite straight forward, isn't it?
if its a small basic type - primatives like int and long and their wrappers and other basic things like 'Point' - return a copy
if its a string, or any other complex type - return a reference.
The only problem I have with returning a const-reference, which is something I would typically do for non basic types, is that there is nothing to stop the caller removing the "const"ness and then modifying the value.
Personally, I'd suggest that such code is a bug. If they know you're returning a reference and continue to cast away the const then it's on their head.