Are bool arrays treated differently in terms of decaying? - c++

Consider an example where I want to create an array of arrays of bool:
int main() {
using type = bool[1];
bool a1[1] = {true};
bool a2[1] = {true};
bool a3[1] = {true};
type block_types[3] = {{a1}, {a2}, {a3}};
}
This code compiles for Clang 7.0.0, GCC 8.2 and MSVS v19.16.
Now, let's change bool to int:
int main() {
using type = int[1];
int a1[1] = {1};
int a2[1] = {1};
int a3[1] = {1};
type block_types[3] = {{a1}, {a2}, {a3}};
}
Now, the code stops compiling on any of those compilers, with errors similar to:
error: invalid conversion from 'int*' to 'int' [-fpermissive]
type block_types[3] = { {a1}, {a2}, {a3}};
^
Note: this exact error message comes from GCC 8.1.
Why is that? Why are bool[]s treated differently than int[]s? What's the rationale behind this?

In the first, block_types is an array of three arrays of one boolean. Each of the inner blocks in your initializer needs to provide a boolean value. You've given a pointer for each one, and pointers will convert to bool.
In the second, block_types is an array of three arrays of one integer, and each inner block of your initializer needs to provide an integer value. You have again given pointers, and they don't convert to integer.
So, no: the arrays are decaying on the same rules, but resulting pointer conversions distinguish the examples.

Related

Why can't we implicitly convert a noconst-elements array to a full-const-elements array?

Here is an example of what I mean (I am using VS 2022 with C++11):
// Printing function: does not modify any array member, so everything should be const
void PrintArray(int const*const (&ct)[3])
{
printf("ct: x%p, {%u, %u, %u}\n",
&ct, ct[0] ? *ct[0] : 0, ct[1] ? *ct[1] : 0, ct[2] ? *ct[2] : 0);
}
int main(int argc, unsigned short* argv[])
{
// At some point: declare (and use) a modifiable array
int a = 1, b = 2, c = 3;
int* t[3] = {&a, &b, &c};
// At some later point: want to use the array without modifying it,
// then lets try some conversions
int* (&u)[3] = t; // OK - Same type conversion
int*const (&v)[3] = t; // OK - adding first constness on array elements
int const*const (&w)[3] = t; // FAIL: adding both constnesses on elements:
// error C2440 "cannot convert from 'int *[3]' to
//'const int *const (&)[3]" (ON VS 2022 C++ 11)
int const*const (&x)[3] = const_cast<int const*const(&)[3]>(t);
// FAIL: same error C2440
int const*const (&y)[3] = reinterpret_cast<int const*const(&)[3]>(t);
// OK - but annoyingly verbose and source of casting
// errors
// Of course the same problem happens for passing through functions call
PrintArray(t); // FAIL
PrintArray(reinterpret_cast<int const*const(&)[3]>(t)); // OK... but still annoying !
return 0;
}
OK, we know that reinterpret_cast is working, so I have a workaround here. So my question is more theoritical about C++ language:
Does someone know if the implicit conversion compiles on other compilers, like gcc ?
Does someone know if the C++ standard forbid this failing implicit conversion ?
And mostly, if the standard does forbid it, WHY ??? I KNOW that const conversions can lead to const/unconst inconsistencies sometimes. E.g. would have been the case if converting t to int const* (&)[3] because we could have added a const int into an array that is not const at the begining. But, in my example above, everything is const so I cannot find any weakness. Does someone see one ? Or is it just because it was too complex at the time for compilers to assert the rightness of the conversion ?
(Additional question for the braves: why cons_cast is failing here while it is only a constness casting ?)
These questions are for my C++ culture, I am open to all your analysis :)
Kind regards to everybody.

C++ pointer of array: why cannot use an array defined by indirectly computed const var in function begin(array)?

Error message in text:
I'm studying the book C++ Primer and encountering a problem listed below when coding an answer for one exercise:
#include<iostream>
#include<vector>
using namespace std;
int main() {
int i = 3;
const int ci = 3;
size_t si = 3;
const size_t csi = 3;
int ia[i];
int cia[ci];
int sia[si];
int csia[csi];
int another_a[] = {1,2,3};
int *pi = begin(ia); // error here
// no instance of overloaded function "begin" matches the argument list --
// argument types are: (int [i])
int *pci = begin(cia);
int *psi = begin(sia); // error here
// no instance of overloaded function "begin" matches the argument list --
// argument types are: (int [si])
int *pcsi = begin(csia);
int *p_ano = begin(another_a);
vector<int> v = {1,3,4};
const int m = v.size();
const size_t n = v.size();
int ma[m];
int na[n];
int *pm = begin(ma); // error here
// no instance of overloaded function "begin" matches the argument list --
// argument types are: (int [m])
int *pn = begin(na); // error here
// no instance of overloaded function "begin" matches the argument list --
// argument types are: (int [n])
system("pause");
return 0;
}
I can understand that the first two errors are because that those two arrays are not defined using an constant variable.
But why the last two, even if I have converted the size of the vector into a constant variable, the compiler still reports an error?
I'm quite confused about this, I would appreciate a lot for your kindly answer or discussion no matter it works or not.
First and foremost, you are using a compiler extension, but more on that later.
The standard begin overload which works for you is a template that accepts a reference to an array with a size that is a constant expression. In a nutshell, constant expressions are those expressions that a compiler can evaluate and know the value of during compilation.
A constant integer initialized with a constant expression like const int ci = 3;, can be used wherever a constant expression is required. So ci is, for all intents an purposes, a constant expression itself (equal to 3).
Modern C++ has a way to make such varaibles stand out as intended constant expressions, it's the constexpr specifier. So you could define ci like this:
constexpr int ci = 3;
It's exactly like your original code. But the same will not work for const int m = v.size();. Because constexpr requires a true constant expression as an initializer, unlike const. For a const variable is not necessarily a constant expression. It can just be a run-time variable that you cannot modify. And this is the case with m.
Because m is not a constant expression, what you defined is a variable length array. A C feature that is sometimes introduced as an extension by C++ compilers. And it doesn't gel with the std::begin template, which expects the array extent to be a constant expression.
Declaring arrays with non constant indexes isn't standard c++.
If you need dynamically sized arrays use std::vector.
Declaring a variable as const doesn't make it a compile time constant (required to declare a fixed sized array) it just means you can't modify it after it is declared.

Why can a const value not be changed in C++ but in C? [duplicate]

This question already has answers here:
how to avoid changing value of const in C
(4 answers)
Closed 8 years ago.
In an interview, I was asked to change constant value in CPP, but I said in CPP it is not possible but in c it is possible using pointer.
Interviewer said that using CPP it is possible and asked me to try but I couldn't and I came back to my room and tried again but what I figured out that I was able to change in C but same code was getting error when compiled as C++.
#include<stdio.h>
main()
{
const int i=5;
int *p;
p=&i;
*p=8;
printf("%d",i);
}
This code is changing the constant value of i in c but when I compile in CPP then
I get an error:
invalid conversion from 'const int*' to 'int*'
Given your error, the actual program must have been the following:
#include<stdio.h>
main()
{
const int i=5;
int *p;
p=&i;
*p=8;
printf("%d",i);
}
This produces a warning with gcc:
warning: assignment discards 'const' qualifier from pointer target type
and an error with g++:
error: invalid conversion from 'const int*' to 'int*'
So, let's change the title of your question to a better one:
Why does C allow conversion from const int * to int *, but C++ doesn't?
The reason why one gives a warning and another gives an error is not because one allows you to discard const qualifier and the other doesn't. It's merely because the C standard leaves such incorrect actions as undefined behavior, while the C++ standard specifically marks it as an error. Either way, doing this is wrong.
You can read this similar question asking why this is possible in C.
What I think interviewer wanted this:
int n = 0;
int const *p = &n;
The expression &n has type “pointer to int.” The declaration for p converts &n to type “pointer to const int,” adding a const qualifier in the process. This is a valid qualification conversion. This conversion in no way invalidates n’s declaration. The program can still use n to alter the int object, even if it can’t use *p for the
same purpose.
*p = 5; // wrong
But
n = 5; // OK
now *p is 5 although it is const type!
Now try to run this code in GCC or g++, it will work:
#include<stdio.h>
int main()
{
int n = 0;
const int *p;
p=&n;
printf("%d\n",*p);
n = 5;
printf("%d\n",*p);
return 0;
}
EDIT: The only way to change the value of const qualified object in C and C++ both is, change the value in the initialization statement:
const int i = 5 ---> const int i = 8
This is why const_cast exists, I believe the interviewer asked this because they have to deal with poorly designed library code they have no control over, however normally you shouldn't have to resort to using it in production.

How to convert int to const int to assign array size on stack?

I am trying to allocate a fixed size on stack to an integer array
#include<iostream>
using namespace std;
int main(){
int n1 = 10;
const int N = const_cast<const int&>(n1);
//const int N = 10;
cout<<" N="<<N<<endl;
int foo[N];
return 0;
}
However, this gives an error on the last line where I am using N to define a fixed
error C2057: expected constant expression.
However, if I define N as const int N = 10, the code compiles just fine.
How should I typecast n1 to trat it as a const int?
I tried : const int N = const_cast<const int>(n1) but that gives error.
EDIT : I am using MS VC++ 2008 to compile this... with g++ it compiles fine.
How should I typecast n1 to treat it as a const int?
You cannot, not for this purpose.
The size of the array must be what is called an Integral Constant Expression (ICE). The value must be computable at compile-time. A const int (or other const-qualified integer-type object) can be used in an Integral Constant Expression only if it is itself initialized with an Integral Constant Expression.
A non-const object (like n1) cannot appear anywhere in an Integral Constant Expression.
Have you considered using std::vector<int>?
[Note--The cast is entirely unnecessary. Both of the following are both exactly the same:
const int N = n1;
const int N = const_cast<const int&>(n1);
--End Note]
Only fixed-size arrays can be allocated that way. Either allocate memory dynamically (int* foo = new int[N];) and delete it when you're done, or (preferably) use std::vector<int> instead.
(Edit: GCC accepts that as an extension, but it's not part of the C++ standard.)

non-const pointer argument to a const double pointer parameter

The const modifier in C++ before star means that using this pointer the value pointed at cannot be changed, while the pointer itself can be made to point something else. In the below
void justloadme(const int **ptr)
{
*ptr = new int[5];
}
int main()
{
int *ptr = NULL;
justloadme(&ptr);
}
justloadme function should not be allowed to edit the integer values (if any) pointed by the passed param, while it can edit the int* value (since the const is not after the first star), but still why do I get a compiler error in both GCC and VC++?
GCC: error: invalid conversion from int** to const int**
VC++: error C2664: 'justloadme' : cannot convert parameter 1 from 'int **' to 'const int **'. Conversion loses qualifiers
Why does it say that the conversion loses qualifiers? Isn't it gaining the const qualifier? Moreover, isn't it similar to strlen(const char*) where we pass a non-const char*
As most times, the compiler is right and intuition wrong. The problem is that if that particular assignment was allowed you could break const-correctness in your program:
const int constant = 10;
int *modifier = 0;
const int ** const_breaker = &modifier; // [*] this is equivalent to your code
*const_breaker = & constant; // no problem, const_breaker points to
// pointer to a constant integer, but...
// we are actually doing: modifer = &constant!!!
*modifier = 5; // ouch!! we are modifying a constant!!!
The line marked with [*] is the culprit for that violation, and is disallowed for that particular reason. The language allows adding const to the last level but not the first:
int * const * correct = &modifier; // ok, this does not break correctness of the code