Why does a function declaration with a const argument allow calling of a function with a non-const argument?
Because it doesn't matter to the caller of the foo
function whether foo
modifies its copy of the variable or not.
Specifically in the C++03 standard, the following 2 snippets explain exactly why:
C++03 Section: 13.2-1
Two function declarations of the same name refer to the same function if they are in the same scope and
have equivalent parameter declarations (13.1).
C++03 Section: 13.1-3
Parameter declarations that differ only in the presence or absence of const and/or volatile are equivalent. Only the const and volatile type-specifiers at the outermost level of the parameter type specification are ignored in this fashion; const and volatile type-specifiers buried within a parameter type specification are significant and can be used to distinguish overloaded function declarations.
Calling a non-const function with a const pointer as argument
int * const ptr=&x;
declares a immutable pointer (you can't change the address stored in ptr
) that points to a mutable int
(you can change the value ptr
points to).
void func(int *n)
takes a pointer to a mutable int
by value, no problems here. The mutability of ptr
is of no concern, because you take a copy anyway. Speaking of taking copies:
n = &k;
will only modify where the local variable n
points. To reflfect changes outside the function you can pass the parameter by reference:
// V
void func(int *&n)
And get the expected compiler error, because you can't bind ptr
to a non-const reference.
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.
Same function with const and without - When and why?
But why would you have both functions in one and the same class definition?
Having both allows you to:
- call the function on a mutable object, and modify the result if you like; and
- call the function on a
const
object, and only look at the result.
With only the first, you couldn't call it on a const
object. With only the second, you couldn't use it to modify the object it returns a reference to.
And how does the compiler distinguish between these?
It chooses the const
overload when the function is called on a const
object (or via a reference or pointer to const
). It chooses the other overload otherwise.
I believe that the second f() (with const) can be called for non-const variables as well.
If that were the only overload, then it could. With both overloads, the non-const
overload would be selected instead.
What is meant with const at end of function declaration?
A "const function", denoted with the keyword const
after a function declaration, makes it a compiler error for this class function to change a member variable of the class. However, reading of a class variables is okay inside of the function, but writing inside of this function will generate a compiler error.
Another way of thinking about such "const function" is by viewing a class function as a normal function taking an implicit this
pointer. So a method int Foo::Bar(int random_arg)
(without the const at the end) results in a function like int Foo_Bar(Foo* this, int random_arg)
, and a call such as Foo f; f.Bar(4)
will internally correspond to something like Foo f; Foo_Bar(&f, 4)
. Now adding the const at the end (int Foo::Bar(int random_arg) const
) can then be understood as a declaration with a const this pointer: int Foo_Bar(const Foo* this, int random_arg)
. Since the type of this
in such case is const, no modifications of member variables are possible.
It is possible to loosen the "const function" restriction of not allowing the function to write to any variable of a class. To allow some of the variables to be writable even when the function is marked as a "const function", these class variables are marked with the keyword mutable
. Thus, if a class variable is marked as mutable, and a "const function" writes to this variable then the code will compile cleanly and the variable is possible to change. (C++11)
As usual when dealing with the const
keyword, changing the location of the const key word in a C++ statement has entirely different meanings. The above usage of const
only applies when adding const
to the end of the function declaration after the parenthesis.
const
is a highly overused qualifier in C++: the syntax and ordering is often not straightforward in combination with pointers. Some readings about const
correctness and the const
keyword:
Const correctness
The C++ 'const' Declaration: Why & How
Passing const to function with non-const parameter
Take a look at this line:
return grade(midterm, final, median(hw));
hw
here is a const reference to a vector, which is being passed to median
. While it is true that you can't modify an object through a const reference, you can still make a copy of it through a const reference. So, since median
requires to change the vector
(by sorting it), it makes a copy for itself (the copy will be implicitly created under the hood) and sorts it instead.
Calling a non const function on a const instance in C++, is that possible?
The "Foo" class added above is purely for demonstrative purpose. In my real code, I can't edit the class, but the reason I want to make "f" const is for code-style/safety reason.
Since
void print() { cout << "hello\n"; }
isn't declared as const
member function of Foo
and you can't change that, you'll have to swallow that poor design, and access the print()
function through a non const
instance or reference.
To do that would need that print()
is declared in the Foo
class like
void print() const { cout << "hello\n"; }
// ^^^^^
I agree that the better code style would be to allow calling the print()
function using a const
instance of Foo
.
But as you mentioned, if you can't change that, there's no way beyond using an explicit cast:
const_cast<Foo&>(f).print();
Anyways that won't add additional "safety" vs a simple call:
int main()
{
Foo f = get_foo();
f.print();
}
Passing non-const arguments to const function parameters in C
The issue you're having stems from your main
function.
When you declare char *my_string = "my text";
,you are creating a non-const pointer to a string literal. By design, string literals such as "my text"
are immutable, and therefore const in the language. (In practice, the compilers usually put the string literals into a specific section of the executable which contains read-only memory, so attempting to modify the literal using the non-const pointer can lead to a segfault.)
By declaring a non-const pointer to the string literal, you end up with a pointer which you could use to modify the immutable string literal, which is considered undefined behavior in C.
See this question for more information.
The easiest way to solve this is to simply change char *my_string
into const char *my_string
.
Is it possible to call a consteval function with a non-const reference parameter?
but
int&
a forces it to be a runtime expression because of the missingconst
No, that's a gross oversimplification and not how constant evaluation works. We can have moving parts (non-const qualified objects) as part of the evaluation. So long as they obey a strict set of rules that are checked when evaluating the constant expression. For example:
consteval void func(int& a) { a = 2; }
consteval int func2() { int b = 0; func(b); return b; }
int arr[func2()];
That's a pretty convoluted way of returning 2
for an array size, but it demonstrates the concept and one of the aforementioned rules. While doing constant evaluation, we introduced a helper variable b
. We then proceeded to do something with it, modifying it, and return the result. That's the "evaluation" part.
The "constant" bit is in the expression truly being evaluatble during translation, all of its "inputs" are compile time constant (vacuously). And any non-const objects we used only came into being while doing the evaluation, not living longer than until it is completed.
Related Topics
Is There a Standard Date/Time Class in C++
Incomplete Class Usage in Template
How to Cheaply Assign C-Style Array to Std::Vector
Glut Deprecation in MAC Osx 10.9, Ide: Qt Creator
When Would You Use an Array Rather Than a Vector/String
Can Void* Be Used to Store Function Pointers
Setting Pointer to Arbitrary Dimension Array
C++ - Std::Thread Crashes Upon Execution
How Should One Use Std::Optional
Should Every Class Have a Virtual Destructor
How to Tell Reliably If a Boost Thread Has Exited Its Run Method
Speed Difference Between If-Else and Ternary Operator in C...
Why Implicit Conversion Is Harmful in C++
Why Do Lambda Functions Drop Deduced Return Type Reference by Default
C++ Static Const Access Through a Null Pointer
In C++ 11, How to Invoke an Arbitrary Callable Object
Constraining the Existing Boost.Spirit Real_Parser (With a Policy)
Undefined Symbol on a Template Operator Overloading Function