I have some code which has been written without regard for const correctness. Are there any circumstances in which changing this
class X
{
public:
X(X& rhs); // does not modify rhs
...
};
to this
class X
{
public:
X(const X& rhs);
...
};
would change the behaviour of an existing program? I know this change will allow code which doesn't currently compile to compile, but I'm interested if there is any circumstance in which code that already compiles would change it's behaviour.
Similar question, is there any merit in making this change instead?
class X
{
public:
X(X& rhs); // does not modify rhs
X(const X& rhs);
...
};
For copy constructor I don't think so. But note that in general, yes, declaration of const can affect which method is called. The only example that comes to mind is with array overloading - see e.g. this question.
In fact a copy constructor should, imho, always take a const reference as its argument.
X(X& rhs) { } // does not modify rhs
This does not allow to copy const objects and hence the following code will not compile.
While non-const objects can serve as const arguments, the other way round is impossible
X const test;
X new_x(test);
I can't imagine why someone should preclude the copy of a const object.
Concerning the changes you want to make:
Does the copy constructor rely on any X member function that is defined non-const?
This will work like a charm but permit copying const objects:
class X
{
private:
int a;
public:
X(X &rhs) { a = rhs.value(); }
int& value (void) { return a; }
};
The next example will not compile since rhs is const but value() is not const.
class X
{
private:
int a;
public:
X(X const &rhs) { a = rhs.value(); }
int& value (void) { return a; }
};
If you want to make your class const correct you'll probably have to examine the whole class.
It should only affect your in-class-implementations. Since I don't know a case where external code should rely on "non-constness" of a class member function.
Except when non-const-references are returned by any public member-functions as in my example.
The following snippet will do as intended.
class X
{
private:
int a;
public:
X(int const &b) : a(b) { }
X(X const &rhs) { a = rhs.value(); }
int const & value (void) const { return a; }
};
But be aware that this will interfere with any code like:
X test(100);
test.value() = 12;
This would work using int& value (void) { return a; } but fails with int const & value (void) const { return a; }.
You could of course provide both to be on the safe side.
Since you are changing the class that has the copy constructor I am assuming you can inspect the copy constructors code. If you can make this change and the copy constructor does not give a compiler error you are probably good. One conor case to consider is what the copy assignment operator does as there is no assurance which will be called especially in optimized code. So also make sure that your copy assignment will work with a const parameter.
djechlin is making an important point in his answer, although somewhat unclear, so I will try to explain better.
The constness of an object or reference affects overload resolution. For instance, if you have an object with a const based overload of a member function, different overloads will be chosen:
struct foo {
void do_stuff();
void do_stuff() const;
};
int main() {
foo f;
const foo& fr = f;
f.do_stuff(); // calls non-const version
fr.do_stuff(); // calls const version
}
Now, if one of the overloads has side-effects the other doesn't, you'd get different behavior after changing the signature, given that (or rather even though) the program compiles fine.
Related
This question already has answers here:
How do I remove code duplication between similar const and non-const member functions?
(21 answers)
C++ template to cover const and non-const method
(7 answers)
Closed 5 years ago.
Is there any advantage using one over the other:
class Foo
{
public:
const int& get() const
{
// stuff here
return myInt;
}
int& get()
{
return const_cast<int&>(static_cast<const Foo*>(this)->get());
}
};
Or
class Foo
{
public:
int& get()
{
// stuff here
return myInt;
}
const int& get() const
{
return const_cast<Foo*>(this)->get();
}
};
I only used the first one, but I just saw the second one used somewhere, so I am wondering.
The comment // stuff here could be a non-trivial check like retrieving the index of a table in order to return a ref on a member of the table (for example: myInt = myTable[myComputedIndex];) so I cannot just make it public. Thus table and any member are not const.
If you have to make a function that is const-agnostic, and avoids duplication, one neat way to do it is delegating implementation to a template, for example
class Foo {
private:
int my_int;
template <typename ThisPtr>
static auto& get(ThisPtr this_ptr) {
return this_ptr->my_int;
}
public:
int& get() {
return get(this);
}
const int& get() const {
return get(this);
}
};
This way you are free from the fear associated with using const_cast, mutable and other stuff that goes into trying to reduce code duplication in cases like this. If you get something wrong, the compiler will let you know.
Ignoring the issue of whether you really need a getter, the best solution when duplicating functionality in both a const and non-const method is to have the non-const method call the const method and cast away the const-ness of the result (i.e. the first of the two alternatives you present in the question).
The reason is simple: if you do it the other way around (with the logic in the non-const method), you could accidentally end up modifying a const object, and the compiler won't catch it at compile time (because the method is not declared const) - this will have undefined behaviour.
Of course this is only a problem if the "getter" is not actually a getter (i.e. if it is doing something more complicated than just returning a reference to a private field).
Also, if you are not constrained to C++11, the template-based solution presented by Curious in their answer is another way of avoiding this problem.
Is there any advantage using one over the other: ...
No, both are bad because they violate the data encapsulation principle.
In your example you should rather make myInt a public member.
There's no advantage to have getters for such case at all.
If you really want (need) getter and setter functions these should look like this:
class Foo
{
private:
mutable int myInt_;
// ^^^^^^^ Allows lazy initialization from within the const getter,
// simply omit that if you dont need it.
public:
void myInt(int value)
{
// Do other stuff ...
myInt = value;
// Do more stuff ...
}
const int& myInt() const
{
// Do other stuff ...
return myInt_;
}
}
You don't say where myInt comes from, the best answer depends on that.
There are 2+1 possible scenarios:
1) The most common case is that myInt comes from a pointer internal to the class.
Assuming that, this is the best solution which avoids both code duplication and casting.
class Foo{
int* myIntP;
...
int& get_impl() const{
... lots of code
return *myIntP; // even if Foo instance is const, *myInt is not
}
public:
int& get(){return get_impl();}
const int& get() const{return get_impl();}
};
This case above applies to pointer array, and (most) smart pointers.
2) The other common case is that myInt is a reference or a value member, then the previous solution doesn't work.
But it is also the case where a getter is not needed at all.
Don't use a getter in that case.
class Foo{
public:
int myInt; // or int& myInt;
};
done! :)
3) There is a third scenario, pointed by #Aconcagua, that is the case of an internal fixed array. In that case it is a toss-up, it really depends what you are doing, if finding the index is really the problem, then that can be factored away. It is not clear however what is the application:
class Foo{
int myInts[32];
...
int complicated_index() const{...long code...}
public:
int& get(){return myInts[complicated_index()];}
const int& get() const{return myInts[complicated_index()];}
};
My point is, understand the problem and donĀ“t over engineer. const_cast or templates are not needed to solve this problem.
complete working code below:
class Foo{
int* myIntP;
int& get_impl() const{
return *myIntP; // even if Foo instance is const, *myInt is not
}
public:
int& get(){return get_impl();}
const int& get() const{return get_impl();}
Foo() : myIntP(new int(0)){}
~Foo(){delete myIntP;}
};
#include<cassert>
int main(){
Foo f1;
f1.get() = 5;
assert( f1.get() == 5 );
Foo const f2;
// f2.get() = 5; // compile error
assert( f2.get() == 0 );
return 0;
}
As you intend access to more complex internal structures (as clarified via your edit; such as providing an operator[](size_t index) for internal arrays as std::vector does), then you will have to make sure that you do not invoke undefined behaviour by modifying a potentially const object.
The risk of doing so is higher in the second approach:
int& get()
{
// stuff here: if you modify the object, the compiler won't warn you!
// but you will modify a const object, if the other getter is called on one!!!
return myInt;
}
In the first variant, you are safe from (unless you do const_cast here, too, which now would really be bad...), which is the advantage of this approach:
const int& get() const
{
// stuff here: you cannot modify by accident...
// if you try, the compiler will complain about
return myInt;
}
If you actually need to modify the object in the non-const getter, you cannot have a common implementation anyway...
Modifying a const object through a non-const access path [...] results in undefined behavior.
(Source: http://en.cppreference.com/w/cpp/language/const_cast)
This means that the first version can lead to undefined behavior if myInt is actually a const member of Foo:
class Foo
{
int const myInt;
public:
const int& get() const
{
return myInt;
}
int& get()
{
return const_cast<int&>(static_cast<const Foo*>(this)->get());
}
};
int main()
{
Foo f;
f.get() = 10; // this compiles, but it is undefined behavior
}
The second version would not compile, because the non-const version of get would be ill-formed:
class Foo
{
int const myInt;
public:
int& get()
{
return myInt;
// this will not compile, you cannot return a const member
// from a non-const member function
}
const int& get() const
{
return const_cast<Foo*>(this)->get();
}
};
int main()
{
Foo f;
f.get() = 10; // get() is ill-formed, so this does not compile
}
This version is actually recommended by Scott Meyers in Effective C++ under Avoid Duplication in const and Non-const Member Function.
Can you look at this example code:
class Test {
int *a;
int b;
public:
Test() : b(2)
{
a = new int(5);
}
const int * const& GetA() const
{
const int * const& c = a;
return a;
}
const int& GetB()
{
return b;
}
~Test()
{
delete a;
}
};
And I get a warning on return a. Why is it wrong to return a reference to a const pointer to a const variable, but it's fine to return a reference to a const variable? By the way if I return c in GetA() it compiles just fine.
Consider first const int& GetB(). That's a neat way of returning a reference to the class member b that can't be modified at the call site. You may as well mark that function const since you are not changing any of the class member data. This is idiomatic C++, especially for types larger than an int, e.g. std::string.
When you write return a;, you are permitting the function call site to modify that class member through that pointer. Although the C++ standard allows this, it circumvents encapsulation and your friendly compiler is warning you of that. Note that since the function itself is not changing the class member, compilation passes despite it being marked as const.
In writing const int * const& c = a; the compiler assumes you know what you're doing.
(As a final note, all havoc is let loose if you attempt to copy an instance of Test due to the compiler generated copy constructor shallow-copying a. You ought to delete the copy constructor and the assignment operators.)
The problem I am trying to address arises with making containers such as an std::vector of objects that contain reference and const data members:
struct Foo;
struct Bar {
Bar (Foo & foo, int num) : foo_reference(foo), number(num) {}
private:
Foo & foo_reference;
const int number;
// Mutable member data elided
};
struct Baz {
std::vector<Bar> bar_vector;
};
This won't work as-is because the default assignment operator for class Foo can't be built due to the reference member foo_reference and const member number.
One solution is to change that foo_reference to a pointer and get rid of the const keyword. This however loses the advantages of references over pointers, and that const member really should be const. They are private members, so the only thing that can do harm is my own code, but I have shot myself in the foot (or higher) with my own code.
I've seen solutions to this problem on the web in the form of swap methods that appear to be chock full of undefined behavior based on the wonders of reinterpret_cast and const_cast. It happens that those techniques do appear to work on my computer. Today. With one particular version of one particular compiler. Tomorrow, or with a different compiler? Who knows. I am not going to use a solution that relies on undefined behavior.
Related answers on stackoverflow:
Does it make sense to implement the copy-assignment operator in a class with all const data members?
The first answer has the amusing line "If that one is immutable, your screwed."
Swap method with const members
The first answer doesn't really apply here, and the second is a bit of a kludge.
So is there a way to write a swap method / copy constructor for such a class that does not invoke undefined behavior, or am I just screwed?
Edit
Just to make it clear, I already am quite aware of this solution:
struct Bar {
Bar (Foo & foo, int num) : foo_ptr(&foo), number(num) {}
private:
Foo * foo_ptr;
int number;
// Mutable member data elided
};
This explicitly eliminates the constness of number and the eliminates the implied constness of foo_reference. This is not the solution I am after. If this is the only non-UB solution, so be it. I am also quite aware of this solution:
void swap (Bar & first, Bar & second) {
char temp[sizeof(Bar)];
std::memcpy (temp, &first, sizeof(Bar));
std::memcpy (&first, &second, sizeof(Bar));
std::memcpy (&second, temp, sizeof(Bar));
}
and then writing the assignment operator using copy-and-swap. This gets around the reference and const problems, but is it UB? (At least it doesn't use reinterpret_cast and const_cast.) Some of the elided mutable data are objects that contain std::vectors, so I don't know if a shallow copy like this will work here.
You can't reseat the reference. Just store the member as a pointer, as it is done in all other libraries with assignable classes.
If you want to protect yourself from yourself, move the int and the pointer to the private section of a base class. Add protected functions to only expose the int member for reading and a reference to the pointer member (e.g to prevent yourself from treating the member as an array).
class BarBase
{
Foo* foo;
int number;
protected:
BarBase(Foo& f, int num): foo(&f), number(num) {}
int get_number() const { return number; }
Foo& get_foo() { return *foo; }
const Foo& get_foo() const { return *foo; }
};
struct Bar : private BarBase {
Bar (Foo & foo, int num) : BarBase(foo, num) {}
// Mutable member data elided
};
(BTW, it doesn't have to be a base class. Could also be a member, with public accessors.)
If you implement this with move operators there is a way:
Bar & Bar :: operator = (Bar && source) {
this -> ~ Bar ();
new (this) Bar (std :: move (source));
return *this;
}
You shouldn't really use this trick with copy constructors because they can often throw and then this isn't safe. Move constructors should never ever throw, so this should be OK.
std::vector and other containers now exploit move operations wherever possible, so resize and sort and so on will be OK.
This approach will let you keep const and reference members but you still can't copy the object. To do that, you would have to use non-const and pointer members.
And by the way, you should never use memcpy like that for non-POD types.
Edit
A response to the Undefined Behaviour complaint.
The problem case seems to be
struct X {
const int & member;
X & operator = (X &&) { ... as above ... }
...
};
X x;
const int & foo = x.member;
X = std :: move (some_other_X);
// foo is no longer valid
True it is undefined behaviour if you continue to use foo. To me this is the same as
X * x = new X ();
const int & foo = x.member;
delete x;
in which it is quite clear that using foo is invalid.
Perhaps a naive read of the X::operator=(X&&) would lead you to think that perhaps foo is still valid after a move, a bit like this
const int & (X::*ptr) = &X::member;
X x;
// x.*ptr is x.member
X = std :: move (some_other_X);
// x.*ptr is STILL x.member
The member pointer ptr survives the move of x but foo does not.
that const member really should be const
Well, then you can't reassign the object, can you? Because that would change the value of something that you've just said really should not change: before the assigment foo.x is 1 and bar.x is 2, and you do foo = bar, then if foo.x "really should be const" then what's supposed to happen? You've told it to modify foo.x, which really should not be modified.
An element of a vector is just like foo, it's an object that the container sometimes modifies.
Pimpl might be the way to go here. Dynamically allocate an object (the "impl") containing all your data members, including const ones and references. Store a pointer to that object (the "p") in your object that goes in the vector. Then swap is trivial (swap the pointers), as is move assignment, and copy assignment can be implemented by constructing a new impl and deleting the old one.
Then, any operations on the Impl preserve the const-ness and un-reseatable-ness of your data members, but a small number of lifecycle-related operations can act directly on the P.
This however loses the advantages of references over pointers
There is no advantage. Pointers and reference are different, but none is the better one. You use a reference to ensure that there is a valid instance and a pointer if passing a nullptr is valid. In your example you could pass a reference and store a pointer
struct Bar {
Bar (Foo & foo) : foo_reference(&foo) {}
private:
Foo * foo_reference;
};
You can compose your class of members that take care of those restrictions but are assignable themselves.
#include <functional>
template <class T>
class readonly_wrapper
{
T value;
public:
explicit readonly_wrapper(const T& t): value(t) {}
const T& get() const { return value; }
operator const T& () const { return value; }
};
struct Foo{};
struct Bar {
Bar (Foo & foo, int num) : foo_reference(foo), number(num) {}
private:
std::reference_wrapper<Foo> foo_reference; //C++11, Boost has one too
readonly_wrapper<int> number;
// Mutable member data elided
};
#include <vector>
int main()
{
std::vector<Bar> bar_vector;
Foo foo;
bar_vector.push_back(Bar(foo, 10));
};
I know this is a relatively old question to dig out, but I have needed something similar recently, and it made me wonder if this is possible to implement using contemporary C++, e.g. C++17.
I've seen this blog post by Jonathan Boccara
and fiddled with it a little, i.e. by adding implicit conversion operators and this is what I have gotten:
#include <optional>
#include <utility>
#include <iostream>
#include <functional>
template <typename T>
class assignable
{
public:
assignable& operator=(const assignable& rhs)
{
mHolder.emplace(*rhs.mHolder);
return *this;
}
assignable& operator=(assignable&&) = default;
assignable(const assignable&) = default;
assignable(assignable&&) = default;
assignable(const T& val)
: mHolder(val)
{}
assignable(T&& val)
: mHolder(std::move(val))
{}
template<typename... Args>
decltype(auto) operator()(Args&&... args)
{
return (*mHolder)(std::forward<Args>(args)...);
}
operator T&() {return *mHolder;}
operator const T&() const {return *mHolder;}
private:
std::optional<T> mHolder;
};
template <typename T>
class assignable<T&>
{
public:
explicit assignable(T& val)
: mHolder(val)
{}
operator T&() {return mHolder;}
operator const T&() const {return mHolder;}
template<typename... Args>
decltype(auto) operator()(Args&&... args)
{
return mHolder(std::forward<Args>(args)...);
}
private:
std::reference_wrapper<T> mHolder;
};
The part some may find controversial is the behaviour of the assignment operator for reference types, but the discussion about rebinding the reference vs the underlying object has been going on there for a while already I think, so I just did according to my taste.
Live demo:
https://godbolt.org/z/WjW7s34jn
I have this code:
template <class T>
class Something
{
T val;
public:
inline Something() : val() {}
inline Something(T v) : val(v) {}
inline T& get() const { return val; }
inline Something& operator =(const Something& a) { val = a.val; return *this; }
};
typedef Something<int> IntSomething;
typedef Something<const int> ConstIntSomething;
class Other
{
public:
IntSomething some_function()
{
return IntSomething(42);
}
ConstIntSomething some_function() const
{
return ConstIntSomething(42);
}
};
void wtf_func()
{
Other o;
ConstIntSomething s;
s = o.some_function();
}
However, the compiler picks the wrong overload of Other::some_function() in wtf_func() (i.e. the non-const one). How can I fix this? Note that for certain reasons I cannot change the name of Other::some_function().
o is not const-qualified, so the non-const some_function is selected. If you want to select the const-qualified overload, you need to add the const qualifier to o:
Other o;
Other const& oref(o);
ConstIntSomething s;
s = oref.some_function();
When overload resolution occurs, the compiler only looks at the o.some_function() subexpression; it does not look at the context around the function call to decide to pick something else. Further, the return type of a member function is not considered during overload resolution.
Note that it may make more sense for IntSomething to be implicitly convertible to ConstIntSomething, either using an operator ConstIntSomething() overload in IntSomething (less good) or using a non-explicit ConstIntSomething(IntSomething const&) constructor in ConstIntSomething (more good).
It doesn't pick the wrong overload; const-ness is resolved by whether this is const or not. In your case, o is non-const, so the non-const overload is picked.
You can hack this by creating a const-reference to o, e.g.:
const Other &o2 = o;
s = o2.some_function();
But really, you should probably be considering your overloads in Something. For instance, you can't currently do this:
IntSomething x;
ConstIntSomething y;
y = x;
which doesn't sound correct. Why shouldn't you be allowed to take a const ref to a non-const ref?
Your object o needs to be const object for a const function to be called on it. Otherwise the compiler rightly picks up the non const version of the function.
The compiler picks the overload to use based on the constness of the object that will become this. You can make it call the desired version with static_cast: s = static_cast<const Other&>(o.some_function());
You might also want to copy the new behaviour found in the containers of the C++0x standard library. Containers such as vector now have members cbegin() and cend() that return a const_iterator whether the container is const or not unlike begin() and end()
class Other {
// Rest of other
public:
// No overload for non-const
// Even if called with a non const Other, since this member is marked
// const, this will be of type Other const * in all cases and will call
// the const qualified overload of some_function.
ConstIntSomething csome_function() const
{
return some_function();
}
};
Recently I have read that it makes sense when returning by value from a function to qualify the return type const for non-builtin types, e.g.:
const Result operation() {
//..do something..
return Result(..);
}
I am struggling to understand the benefits of this, once the object has been returned surely it's the callers choice to decide if the returned object should be const?
Basically, there's a slight language problem here.
std::string func() {
return "hai";
}
func().push_back('c'); // Perfectly valid, yet non-sensical
Returning const rvalues is an attempt to prevent such behaviour. However, in reality, it does way more harm than good, because now that rvalue references are here, you're just going to prevent move semantics, which sucks, and the above behaviour will probably be prevented by the judicious use of rvalue and lvalue *this overloading. Plus, you'd have to be a bit of a moron to do this anyway.
It is occasionally useful. See this example:
class I
{
public:
I(int i) : value(i) {}
void set(int i) { value = i; }
I operator+(const I& rhs) { return I(value + rhs.value); }
I& operator=(const I& rhs) { value = rhs.value; return *this; }
private:
int value;
};
int main()
{
I a(2), b(3);
(a + b) = 2; // ???
return 0;
}
Note that the value returned by operator+ would normally be considered a temporary. But it's clearly being modified. That's not exactly desired.
If you declare the return type of operator+ as const I, this will fail to compile.
There is no benefit when returning by value. It doesn't make sense.
The only difference is that it prevents people from using it as an lvalue:
class Foo
{
void bar();
};
const Foo foo();
int main()
{
foo().bar(); // Invalid
}
Last year I've discovered another surprising usecase while working on a two-way C++-to-JavaScript bindings.
It requires a combination of following conditions:
You have a copyable and movable class Base.
You have a non-copyable non-movable class Derived deriving from Base.
You really, really do not want an instance of Base inside Derived to be movable as well.
You, however, really want slicing to work for whatever reason.
All classes are actually templates and you want to use template type deduction, so you cannot really use Derived::operator const Base&() or similar tricks instead of public inheritance.
#include <cassert>
#include <iostream>
#include <string>
#include <utility>
// Simple class which can be copied and moved.
template<typename T>
struct Base {
std::string data;
};
template<typename T>
struct Derived : Base<T> {
// Complex class which derives from Base<T> so that type deduction works
// in function calls below. This class also wants to be non-copyable
// and non-movable, so we disable copy and move.
Derived() : Base<T>{"Hello World"} {}
~Derived() {
// As no move is permitted, `data` should be left untouched, right?
assert(this->data == "Hello World");
}
Derived(const Derived&) = delete;
Derived(Derived&&) = delete;
Derived& operator=(const Derived&) = delete;
Derived& operator=(Derived&&) = delete;
};
// assertion fails when the `const` below is commented, wow!
/*const*/ auto create_derived() { return Derived<int>{}; }
// Next two functions hold reference to Base<T>/Derived<T>, so there
// are definitely no copies or moves when they get `create_derived()`
// as a parameter. Temporary materializations only.
template<typename T>
void good_use_1(const Base<T> &) { std::cout << "good_use_1 runs" << std::endl; }
template<typename T>
void good_use_2(const Derived<T> &) { std::cout << "good_use_2 runs" << std::endl; }
// This function actually takes ownership of its argument. If the argument
// was a temporary Derived<T>(), move-slicing happens: Base<T>(Base<T>&&) is invoked,
// modifying Derived<T>::data.
template<typename T>
void oops_use(Base<T>) { std::cout << "bad_use runs" << std::endl; }
int main() {
good_use_1(create_derived());
good_use_2(create_derived());
oops_use(create_derived());
}
The fact that I did not specify the type argument for oops_use<> means that the compiler should be able to deduce it from argument's type, hence the requirement that Base<T> is actually a real base of Derived<T>.
An implicit conversion should happen when calling oops_use(Base<T>). For that, create_derived()'s result is materialized into a temporary Derived<T> value, which is then moved into oops_use's argument by Base<T>(Base<T>&&) move constructor. Hence, the materialized temporary is now moved-from, and the assertion fails.
We cannot delete that move constructor, because it will make Base<T> non-movable. And we cannot really prevent Base<T>&& from binding to Derived<T>&& (unless we explicitly delete Base<T>(Derived<T>&&), which should be done for all derived classes).
So, the only resolution without Base modification here is to make create_derived() return const Derived<T>, so that oops_use's argument's constructor cannot move from the materialized temporary.
I like this example because not only it compiles both with and without const without any undefined behaviour, it behaves differently with and without const, and the correct behavior actually happens with const only.