how to use a rvalue reference in a switch's case - c++

I want to use t(rvalue reference) in a case of switch(T), but I get an error the value of 't' is not usable in a constant expression. How to do it the right way.
#include <iostream>
using namespace std;
int main(){
int (&&t)=5;
int T{};
switch(T){
case t: // error in this case
cout<<t<<endl;
break;
default:
cout<<"default"<<endl;
break;
}
}

Note that case labels in a switch statement need to be constant expressions.
You can't use an rvalue reference as a case label, since it is not a constant expression. You can however use a compile time initialized variable:
constexpr int t = 5;
switch(...)
{
case t : ... // ok
}
Note that trying to do something like:
int const &&t = 5;
will not work either, since the initializer is not a constant expression, so there is no way that I'm aware of that lets you use an rvalue reference as a case label.
Note also, that something like:
int const t = ...
will not work unless the initializer is a constant expression (i.e. known at compile time). So:
int const t = 5;
will work, but:
int n = 5;
int const t = n;
will not.

Related

Why can't we initialize a "constexpr variable" inside a Switch (below a non last SWITCH CASE)?

As we know that we can not initialize a variable in any of the case in switch, unless it is the last case of the respective switch, as initialization of variables does require the definition to execute at runtime (since the value of the initializer must be determined at that point),
BUT
We also know that a constexpr variable will get initialized or get replaced in the code with its value during the compilation process itself.
So I tried the below code i.e., initializing a constexpr variable Z in case 2 (which is the not the last case of the switch) but I am getting an error stating as:
crosses initialization of ‘constexpr const int z’ 24 |
constexpr int z{ 4 }; // illegal: initialization is not allowed if
subsequent cases exist
May someone please clarify the reason behind this error?
Thanking you in Advance!
#include <iostream>
int main()
{
switch (1)
{
int y;
case 1:
y = 4;
break;
case 2:
constexpr int z{ 4 }; // ERROR
break;
case 3:
y=5;
break;
}
return 0;
}
According to the c++ reference for the switch statement
Because transfer of control is not permitted to enter the scope of a
variable, if a declaration statement is encountered inside the
statement, it has to be scoped in its own compound statement.
This means that you can actually initialize a local variable, constexpr or not, as long as it is inside a scope. For example, a small modification of your code does compile:
#include <iostream>
int main()
{
switch (1)
{
int y;
case 1:
y = 4;
break;
case 2:
{
constexpr int z{ 4 }; // No more ERROR
break;
}
case 3:
y=5;
break;
}
return 0;
}
Check it live on Coliru.
The switch statement jumps to a matching case very much like a goto statement.
Then, like the goto statement, it would be unclear what would happen if, by jumping to a given line, you "skip" the declaration/initialization of the variable.
Consider the following example:
int main()
{
goto jump;
int i = 1;
jump:
}
This does not compile, because in executing the jump, the compiler does not know what to do the variable i. Notice that, if you did not have the jump, it would possible to use the variable i after the jump label. But what if you just "skip" the initialization? Then, the code is malformed.
The switch statement, essentially does a goto and because of that it has the same limitations. The fact the the variable you declare is a constexpr does not change this limitation.
The initialization of a variable with automatic storage duration happens when control transfers over it, regardless of it is a constexpr variable or not.
Consider the following code (if initializing a constexpr variable was allowed in the body of a switch):
void f(const int*);
switch (cond) {
case 1:
constexpr int z{ 4 };
f(&z);
break;
case 2:
f(&z);
break;
}
Even though z is a constexpr variable, a compiler would still put it on the stack to pass its address to f, and it's value on the stack would be assigned 4 at runtime. But what about the case 2? The compiler will allocate z on the stack when it enters the switch, but logically the initializer will only be "ran" if it enters the first case.
You can't "initialize" a constexpr variable at compile time because it will be placed on the stack. Whether it is eliminated entirely and all occurrences of it replaced is an optimization (which isn't always possible if its address is taken).

'Constant Expression Required' Error while keeping formal argument as a constant

This is a C++ programming code to display the values of array1 and array2 but I am getting a compile time error as 'Constant Expression Required'. Please Help
void display(const int const1 = 5)
{
const int const2 = 5;
int array1[const1];
int array2[const2];
for(int i = 1 ; i < 5 ; i++)
{
array1[i] = i;
array2[i] = i * 10;
std::cout << array1[i] << std::endl;
}
}
void main()
{
display(5);
}
In C++, const is not always constexpr. Back in the days, constexpr didn't exist, so the only way of having a compile time constant was to either use const with a literal, or to use enum, because both of these are easy for the compiler to check the value.
However, in C++11, we added constexpr, which guaranties that a constexpr variable has a value available at compile-time, and state that constexpr function can be evaluated aat compile time if all arguments are constexpr too.
In your code, you can write your variable const2 like this:
void display(const int const1=5)
{
constexpr int const2 = 5;
// ...
}
Now your code is much more expressive about what you are doing. instead of relying that the const may be available at compile time, you say "this variable has a value known at compile time, here's the value".
However, if you try to change const1, you'll get an error. Parameters, even with default value always as a value known at runtime. If the value is only known at runtime, you can't use it in template parameters or array size.
If you want your function to be able to receive the value const1 as a constant expression from where you can receive it as a template parameter, since template parameters are always known at compile time.
template<int const1 = 5>
void display()
{
constexpr int const2 = 5;
int array1[const1];
int array2[const2];
}
You will have to call your function like that:
// const1 is 5
display();
// const1 is 10
display<10>();
If you want to know more about templates, go check Function templates, or this tutorial

How do I prevent a const& argument from being changed indirectly?

Is it possible to make a const& truely immutable?
int* side_effect;
void function(int const& i){
*side_effect = 123;
}
int main(){
int i = 0;
side_effect = &i;
//based on the function signature, i is suspected not to change.
//however, that is not the case.
function(i);
}
Are there any compiler warnings, attributes, compiler extensions, or language features I can employ to avoid these kinds of problems?
If you want a value to be a true constant, you can pass it as a template value argument.
template<int i>
void function(){
*side_effect = 123;
}
there is no way for any operation to modify i.
This requires that the input be a compile-time constant (and verified so by the compiler at compile time).
So this doesn't work:
int main(){
int i = 0;
side_effect = &i;
function<i>();
}
as i is not a compile-time constant. If we instead made it:
int main(){
const int i = 0;
side_effect = &i;
function<i>();
}
the function<i> line works, but the side_effect = &i doesn't work. If we add in a cast:
int main(){
const int i = 0;
side_effect = const_cast<int*>(&i);
function<i>();
}
now the operation *side_effect = 123 becomes UB within function.
Another approach which doesn't require that the value being passed in is a true compile-time constant is to not take a reference:
void function(int i){
*side_effect = 123;
}
and instead take a local copy (either int or const int as the argument, depending on if we want function to have the rights to modify i).
The full strength version of what you want -- that we take a reference to external data, and then ensure that the external data remains unchanged -- can pretty easily be shown to be equivalent to the halting problem in the general case.

Switching on scoped enum

I am trying to switch on a scoped-enum with the type unsigned int:
The enum is defined as:
const enum struct EnumType : unsigned int { SOME = 1, MORE = 6, HERE = 8 };
I receive a const unsigned int reference and I am trying to check that value against the enum values.
void func(const unsigned int & num)
{
switch (num)
{
case EnumType::SOME:
....
break;
case EnumType::MORE:
....
break;
....
default:
....
}
}
This results in a syntax error: Error: This constant expression has type "EnumType" instead of the required "unsigned int" type.
Now, using a static_cast on each switch, such as:
case static_cast<unsigned int>(EnumType::SOME):
....
break;
case static_cast<unsigned int>(EnumType::MORE):
....
break;
fixes the syntax error, although casting at each case statement doesn't seem like a good way to do this. Do I really need to cast at each case, or is there a better way?
You can solve this by casting the switch variable itself to EnumType:
switch (static_cast<EnumType>(num)) {
(Demo)
The purpose of scoped enums is to make them strongly-typed. To this end, there are no implicit conversions to or from the underlying type. You have to convert either the switch variable or the switch cases. I would suggest converting the switch variable since this requires less code and therefore will make maintenance easier.
IMO the correct solution would be to change the function to accept const EnumType & (or just EnumType) instead.

Reference to Pointer Type

#include <iostream>
using namespace std;
int main(void)
{
int num[5];
const int* &ref = num;
return 0;
}
I've read a C++ book which mentioned if a reference variable is referencing to:
a variable where the type is different but can be converted.
a variable that is not a Lvalue.
As long as the referencing variable is declare as const, the above 2 cases will be solved by using a method where the compiler will create a storage and the value will be placed into it while the identifier of the referencing variable is treated as the identifier for that particular storage location . Below is the demonstration code .
Case 1
#include <iostream>
using namespace std;
int main(void)
{
int num = 5;
const long long &ref = num; //the value 5 is place in a new storage where ref is the identifier
return 0;
}
Case 2:
#include <iostream>
using namespace std;
int main(void)
{
int num = 5;
const int &ref = num + 1; //expression num + 1 evaluated and store in new storage with identifier ref
return 0;
}
Since this 2 cases is valid, how come the case inside The Code: is invalid?
My logic is since the name of the array when used will be converted to pointer to the first element of the array , thus the compiler should've spotted this is not a lvalue and a new storage will be created to store that address along and of course , the referencing variable name will be taken as the identifier for that location .
Note : I know this is slightly out of topic , but may I know whether an array name is Lvalue or not? Just a simple yes or no will do , since changing the code to int &ref = num I assume it's not a lvalue , but I just need further confirmation.
Thank you.
You reference variable is not declared const.
There's a difference between const int * and int * const, and you've picked the wrong one.
Your example (ii) is invalid for the same reason, it should be const int &ref = num + 1;
For your Note, I'm not sure that a simple yes or no will do. A simple array name is an lvalue, referring to the array. However, in most contexts it decays to a pointer-to-first-element, which is an rvalue pointer.