What's the difference between a const member function and a non-const member function?
In short, they're used to add 'const correctness' to your program.
value_type& top() { return this.item }
This is used to provide mutable access to item
. It is used so you can modify the element in the container.
For example:
c.top().set_property(5); // OK - sets a property of 'item'
cout << c.top().get_property(); // OK - gets a property of 'item'
One common example for this pattern is returning mutable access to an element with vector::operator[int index]
.
std::vector<int> v(5);
v[0] = 1; // Returns operator[] returns int&.
On the other hand:
const value_type& top() const { return this.item }
This is used to provide const
access to item
. It's more restrictive than the previous version - but it has one advantage - you can call it on a const object.
void Foo(const Container &c) {
c.top(); // Since 'c' is const, you cannot modify it... so the const top is called.
c.top().set_property(5); // compile error can't modify const 'item'.
c.top().get_property(); // OK, const access on 'item'.
}
To follow the vector example:
const std::vector<int> v(5, 2);
v[0] = 5; // compile error, can't mutate a const vector.
std::cout << v[1]; // OK, const access to the vector.
Why the need for both const reference and const member function?
- return a const reference to prevent the function to change the returning value
- a const member function that cannot modify the object. in this case, it would be like this
This could have been a bit more clearer, I'll try my shot at explaining it better.
Returning a const reference prevent the returned object to be mutated by callers.
Here's an example:
// let get_name be `const std::vector<std::string>& get_name()`
np1.get_name().size(); // ok, size is a const function of vector
np1.get_name().push_back("hello"); // ERROR! push_back is not a const function of vector
So indeed, a caller cannot change the name
vector. The return type is const qualified.
However, if the function get_name
itself is not const qualified (not the return type), then it is allowed to change name from the class itself.
You see, member functions receive a hidden this
parameter, which is a pointer to the object being called on. The pointer can either point to a const object, or a mutable object. Here's the comparison:
// returning const ref, callers cannot change the name vector
const std::vector<std::string>& get_name() {
// The function receive a `this` that points to a mutable,
// we can change the name from the inside
this->name.push_back("another");
return name;
}
// the `this` pointer points to const -----v---v
const std::vector<std::string>& get_name() const {
this->name.push_back("another"); // ERROR! Cannot mutate member of const object
return name;
}
Const-qualified member function are really useful for the caller, as it knows that whenever this function is called, its state won't change.
For example, not only you know that the vector::size()
function won't mutate the vector, but the compiler guarantees it since it's a const qualified member function.
And for the last bit, the code you posted here:
vector<string> get_name() const { return name; }
This will not return a reference, but will make a copy. The caller can mutate the copy however it wants, but cannot mutate name
itself.
Here's an example of a copy mutated:
auto name_copy = np1.get_name();
name_copy.push_back("another name"); // works, we mutated the copy by adding an element
C++ Const Member Function (Beginner)
The quote is correct.
Try this
class TestClass
{
public:
void nonconst(){};
void constMethod() const {}
};
int main()
{
TestClass const *s = new TestClass();
//s->nonconst(); // (1) no not legal
s->constMethod();
s = new TestClass(); // (2) yes legal
s->constMethod();
}
- s is a pointer to a constant. Calling a non const method causes
passing ‘const TestClass’ as ‘this’ argument discards qualifiers [-fpermissive]
- However s can point to a different instance. As said in the comments the pointer can be pointed to a different variable.
Resolve const and non-const member function pointer
You may use:
template <typename T>
void doWork(void (T::*fun)() const){
const A a;
(a.*fun)();
}
A more generic function template would use const T a
.
template <typename T>
void doWork(void (T::*fun)() const){
const T a;
(a.*fun)();
}
Note that the second version does not assume A
anywhere.
Calling non const method in const method
The point is who is const
in the const
member function, the pointer ? the pointee ?
In the const
member function f
, ptr_
, i.e. the pointer itself is considered as const
, but not the object pointed by it. You're calling non-const member function g
on the pointee, then it's fine.
Furthermore, you can't perform any modification (and call non-const member function) on the pointer ptr_
itself (same as obj_
), like ptr_ = std::make_shared<B>();
; but you can do this on the object pointed by it, like *ptr_ = B{};
.
How to prefer calling const member function and fallback to non-const version?
This seems to work:
#include <type_traits>
#include <iostream>
template<typename T, typename=void>
struct find_a_drink {
void operator()(T &t) const
{
t.get_drink();
}
};
template<typename T>
struct find_a_drink<T,
std::void_t<decltype( std::declval<const T &>()
.get_drink())>
> {
void operator()(T &t) const
{
static_cast<const T &>(t).get_drink();
}
};
template<typename T>
void drink_from(T &&t)
{
find_a_drink<std::remove_reference_t<T>>{}(t);
}
/// v1
class Bar1
{
public:
void get_drink()
{
std::cout << "non-const get_drink() called (Bar1)" << std::endl;
}
};
class Bar2
{
public:
void get_drink()
{
std::cout << "non-const get_drink() called (Bar2)" << std::endl;
}
void get_drink() const
{
std::cout << "const get_drink() called (Bar2)" << std::endl;
}
};
int main()
{
Bar1 b1;
Bar2 b2;
drink_from(b1);
drink_from(b2);
return 0;
}
Result:
non-const get_drink() called (Bar1)
const get_drink() called (Bar2)
Call non-const function on a const object
Yes. Remember two things-
- If the function is non-constant, it can only be called by a
non-constant object. - If the function is constant, it can be called on any objects (I mean
any constant or non-constant objects.)
Reasons:
- If the function is non-constant, then the function is allowed to
change values of the object on which it is being called. So the
compiler doesn't allow to create this chance and prevent you to call
a non-constant function on a constant object, as constant object
means you cannot change anything of it anymore. So the compiler only
allows you to call it on a non-constant object as this object can be
modified. - If the function is constant itself, then it is promising that it
won't change anything of the object on which it is being called. So
the compiler doesn't care whether you are calling a constant
function on a constant or non-constant object as the function itself
is unable to change the object.
Related Topics
Why Are Forward Declarations Necessary
Write a Recursive Function That Reverses the Input String
Cpack: Exclude Install Commands from Subdirectory (Googletest Directory)
C++ Add Months to Chrono::System_Clock::Time_Point
Define Constant Variables in C++ Header
Is There an Online Name Demangler for C++
C++: Overriding Public\Private Inheritance
What Should Std::Vector::Data() Return If the Vector Is Empty
Why the Sizeof(Bool) Is Not Defined to Be One, by the Standard Itself
C++:Friend Declaration 'Declares a Non-Template Function
How to Use Dynamic Name for Variables in C++
Sizeof(Struct) Returns Unexpected Value