Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are const arguments "real" constants?

Tags:

c++

AFAIK removing constness from const variables is undefined behavior:

const int i = 13;
const_cast<int&>(i) = 42;      //UB
std::cout << i << std::endl;   //out: 13

But are const function arguments "real" constants? Let's consider following example:

void foo(const int k){
    const_cast<int&>(k) = 42;    //UB?
    std::cout << k << std::endl;
}

int main(){
    foo(13); //out: 42
}

Seems like compiler doesn't apply the same optimizations to const int k as to const int i.

Is there UB in the second example? Does const int k help compiler to optimize code or compiler just checks const correctness and nothing more?

Example

like image 377
yrHeTateJlb Avatar asked Oct 11 '18 07:10

yrHeTateJlb


People also ask

What is const argument?

A constant argument is the one whose modification cannot take place by the function. Furthermore, in order to make an argument constant to a function, the use of a keyword const can take place like- int sum (const int a, const int b).

Is const a variable?

Constants are block-scoped, much like variables declared using the let keyword. The value of a constant can't be changed through reassignment (i.e. by using the assignment operator), and it can't be redeclared (i.e. through a variable declaration).

Does const exist in C?

Yes, there's a const keyword in C.

Should function parameters be const?

Always use const on function parameters passed by reference or pointer when their contents (what they point to) are intended NOT to be changed. This way, it becomes obvious when a variable passed by reference or pointer IS expected to be changed, because it will lack const .


2 Answers

The i in const int i = 13; can be used as constant expression (as template argument or case label) and attempts to modify it are undefined behavior. It is so for backwards compatibility with pre-C++11 code that did not have constexpr.

The declarations void foo(const int k); and void foo(int k); are declaring same function; the top level const of parameters does not participate in function's signature. Parameter k must be passed by value and so can't be "real" constant. Casting its constness away is not undefined behavior. Edit: But any attempt to modify it is still undefined because it is const object [basic.type.qualifier] (1.1):

A const object is an object of type const T or a non-mutable subobject of such an object.

By [dcl.type.cv] 4 const object can't be modified:

Except that any class member declared mutable (10.1.1) can be modified, any attempt to modify a const object during its lifetime (6.8) results in undefined behavior.

And since function parameters are with automatic storage duration a new object within its storage can't be also created by [basic.life] 10:

Creating a new object within the storage that a const complete object with static, thread, or automatic storage duration occupies, or within the storage that such a const object used to occupy before its lifetime ended, results in undefined behavior.

It is unclear why k was declared const at the first place if there is plan to cast its constness away? The only purpose of it feels to be to confuse and to obfuscate.

Generally we should favor immutability everywhere since it helps people to reason. Also it may help compilers to optimize. However where we only declare immutability, but do not honor it, there it works opposite and confuses.

Other thing that we should favor are pure functions. These do not depend on or modify any external state and have no side-effects. These also are easier to reason about and to optimize both for people and for compilers. Currently such functions can be declared constexpr. However declaring the by-value parameters as const does not help any optimizations to my knowledge, even in context of constexpr functions.

like image 107
Öö Tiib Avatar answered Oct 20 '22 22:10

Öö Tiib


But are const function arguments "real" constants?

I think the answer is yes.

They won't be stored in ROM (for example), so casting away const will at least appear to work OK. But my reading of the standard is that the the parameter's type is const int and therefore it is a const object ([basic.type.qualifier]), and so modifying it is undefined ([dcl.type.cv]).

You can confirm the parameter's type fairly easily:

static_assert( std::is_same_v<const int, decltype(k)> );

Is there UB in the second example?

Yes, I think there is.

Does const int k help compiler to optimize code or compiler just checks const correctness and nothing more?

In theory the compiler could assume that in the following example k is not changed by the call to g(const int&), because modifying k would be UB:

extern void g(const int&);

void f(const int k)
{
  g(k);
  return k;
}

But in practice I don't think compilers take advantage of that, instead they assume that k might be modified (compiler explorer demo).

like image 30
Jonathan Wakely Avatar answered Oct 20 '22 21:10

Jonathan Wakely