Cannot define constexpr pointer with constexpr int variable [duplicate] - c++

This question already has answers here:
Is the address of a local variable a constexpr?
(4 answers)
Closed last month.
int main() {
constexpr int i = 5;
constexpr const int *p = &i;
return 0;
}
After compile the code above with:
g++ main.cpp
Here is the error:
main.cpp: In function ‘int main()’: ║
~ ║main.cpp:3:28: error: ‘& i’ is not a constant expression ║
~ ║ 3 | constexpr const int *p = &i; ║
~ ║ | ^~ ║
~ ║ ║
Here is my g++ version:
║Target: x86_64-pc-linux-gnu ║
║Configured with: /build/gcc/src/gcc/configure --enable-languages=c,c++,a║
~ ║da,fortran,go,lto,objc,obj-c++,d --enable-bootstrap --prefix=/usr --libd║
~ ║ir=/usr/lib --libexecdir=/usr/lib --mandir=/usr/share/man --infodir=/usr║
~ ║/share/info --with-bugurl=https://bugs.archlinux.org/ --with-build-confi║
~ ║g=bootstrap-lto --with-linker-hash-style=gnu --with-system-zlib --enable║
~ ║-__cxa_atexit --enable-cet=auto --enable-checking=release --enable-cloca║
~ ║le=gnu --enable-default-pie --enable-default-ssp --enable-gnu-indirect-f║
~ ║unction --enable-gnu-unique-object --enable-libstdcxx-backtrace --enable║
~ ║-link-serialization=1 --enable-linker-build-id --enable-lto --enable-mul║
~ ║tilib --enable-plugin --enable-shared --enable-threads=posix --disable-l║
~ ║ibssp --disable-libstdcxx-pch --disable-werror ║
~ ║Thread model: posix ║
~ ║Supported LTO compression algorithms: zlib zstd ║
~ ║gcc version 12.2.0 (GCC)
I just put a static keyword before the i as suggestion from IDE and it works, but I really don't understand why.

constexpr doesn't change the storage duration of a variable. Your i is still a local variable with automatic storage duration that lives until the end of the block in which it is defined and each call to the function has a new instance of the variable i (although all of these instances have the same constant value).
Which of these instances should the compile-time constant value of p refer to? It is impossible to decide, so a pointer to an automatic storage duration variable is not allowed to be the result of a constant expression.
static gives the variable static storage duration. There is then only one instance of i during the whole program duration and so it is clear what object the compile-time constant value of p would refer to. So this is allowed. In fact, because your initializer of p does not actually read the value of i, i doesn't need to be constexpr for this.

Related

GCC constexpr allows add but not bitwise-or with address

Consider this code:
#include <cstdint>
static int x = 0;
const uintptr_t arithmetic()
{
static constexpr uintptr_t result = ((uintptr_t)&x) + 1u;
return result;
}
const uintptr_t bitwise()
{
static constexpr uintptr_t result = ((uintptr_t)&x) | 1u;
return result;
}
GCC (all versions 4-9) compiles arithmetic() just fine, but rejects bitwise():
<source>: In function 'const uintptr_t bitwise()':
<source>:13:57: error: '(((uintptr_t)(& x)) | 1)' is not a constant expression
13 | static constexpr uintptr_t result = ((uintptr_t)&x) | 1u;
| ~~~~~~~~~~~~~~~~^~~~
Why? Note that bitwise-or works fine in other constexpr use cases, but not this one.
Demo: https://godbolt.org/z/x5jbuU
You can’t use reinterpret_cast (or a C-style cast that performs one) in a constant expression at all. GCC either has a bug enforcing that, or is trying to be helpful to support some practical use case where + but not | is relevant.

save enumeration type in bit-field [dcl.enum] [class.bit]

In the code below, a variable of enumeration type is saved into a bit-field. As I understand it, variable can be retrieved without data loss. However, that doesn't work.
Is the output below in accordance with the standard?
#include<iostream>
using namespace std;
const int M=3, UL=(1<<M)-1; // range 0 .. 7, fits into 3 bits
enum class ec { e_min=0, e_max=UL};
// in [decl.enum] (8): b_min==0 && b_max==7==2^M-1
struct bitFieldType {
ec data : M; // 3 bits
};
int main(){
bitFieldType bf;
for (int c=0; c<=UL; ++c){;
ec enumIn { static_cast<ec>(c) }; // initialize enumeration type
bf.data = enumIn; // copy into bit-field
ec enumOut{bf.data}; // retrieve enumeration type from bit-field
cout<<static_cast<int>(enumIn) <<" "<< static_cast<int>(enumOut)
<< " " << (bf.data==enumIn) <<"\n";
}
}
[dcl.enum] (8): "The size of the smallest bit-field large enough to hold
all the values of the enumeration type is max(M, 1) if b min is zero ... ."
[class.bit] (4)
" the value of an enumerator is stored into a bit-field of the same
enumeration type and the number of bits in the bit-field is large enough
to hold all the values of that enumeration type (10.2), the original
enumerator value and the value of the bit-field
shall compare equal."
If so, why does output look like this?
clang++ -Wall -fsanitize=undefined -pedantic -std=c++17 bitf.cpp && ./a.out
0 0 1
1 1 1
2 2 1
3 3 1
4 -4 0
5 -3 0
6 -2 0
7 -1 0
clang++ --version
clang version 9.0.0 (trunk 351995)
Target: x86_64-unknown-linux-gnu
EDIT: added a static_cast<>() such that the code compiles with 'enum class' replaced by plain 'enum'.
With plain 'enum' instead of 'enum class', the output is as expected. Also, with one extra bit in the bit-field, the output is as expected.
Your ec, being scoped, has a fixed underlying type of int, so its values are all those of int and your bit-field is not wide enough to guarantee anything. In practice, it’s probably being interpreted as a signed bit-field which needs to be of width 4 to store the values 4–7 (even though the rule about signedness doesn’t explicitly define the signedness of an enumeration type to be that of its underlying type).
I read that too fast. You've got bigger problems to begin with the enum class to begin with. Oddly enough if I remove the "class" from the enum declaration that warning goes away;
Is it safe to use an enum in a bit field?
g++ foo.cpp -o foo
foo.cpp:7:15: warning: ‘bitFieldType::data’ is too small to hold all values of ‘enum class ec’
ec data : M; // 3 bits
^
foo.cpp: In function ‘int main()’:
foo.cpp:12:19: error: cannot convert ‘int’ to ‘ec’ in initialization
ec enumIn { c }; // initialize enumeration type
^
<builtin>: recipe for target 'foo' failed
make: *** [foo] Error 1
g++ --version
g++ (Ubuntu 7.3.0-27ubuntu1~18.04) 7.3.0
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Turns out the default type for enum class is int, which explains the sign extension. Just specify the type and it's fine, still compiles with a warning though.
#include<iostream>
using namespace std;
const int M=3, UL=(1<<M)-1; // range 0 .. 7, fits into 3 bits
enum class ec : unsigned int { e_min=0, e_max=UL};
// in [decl.enum] (8): b_min==0 && b_max==7==2^M-1
struct bitFieldType {
ec data : M; // 3 bits
};
int main(){
bitFieldType bf;
for (int c=0; c<=UL; ++c){;
ec enumIn { (ec)(c) }; // initialize enumeration type
bf.data = enumIn; // copy into bit-field
ec enumOut{bf.data}; // retrieve enumeration type from bit-field
cout<<static_cast<int>(enumIn) <<" "<< static_cast<int>(enumOut)
<< " " << (bf.data==enumIn) <<"\n";
}
}
/*
0 0 1
1 1 1
2 2 1
3 3 1
4 4 1
5 5 1
6 6 1
7 7 1
*/

-fno-strict-aliasing as function attribute

I have a function in which I'm type-punning for performance reasons. Basically, I have a 32-by-32 bit array stored as an array of 32 uint32s:
struct Tile {
uint32_t d[32];
};
I then want to calculate the population (number of '1's) of the 28-by-28 'interior' of the 32-by-32 tile. The naive method would take 28 calls to the machine's popcnt instruction, one for each row. However, since popcnt can take a 64-bit argument, this can be reduced to 14 popcnt calls:
int countPopulation(Tile* sqt) __attribute__((optimize("-fno-strict-aliasing"))) {
int pop = 0;
for (int i = 2; i < 30; i += 2) {
const uint64_t v = *reinterpret_cast<const uint64_t*>(sqt->d + i);
pop += __builtin_popcountll(v & 0x3ffffffc3ffffffcull);
}
return pop;
}
If I don't include the attribute:
__attribute__((optimize("-fno-strict-aliasing")))
then g++ will consistently complain, for obvious reasons, about my type-punning:
warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
const uint64_t v = *reinterpret_cast<const uint64_t*>(sqt->d + i);
On the other hand, if I do include the attribute, certain versions of g++ complain whereas others do not. Of the machines on which I've tried this, I get:
g++ (Ubuntu 4.8.4-2ubuntu1~14.04.1) 4.8.4 complains
g++-4.6.real (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3 does not complain
g++ (Debian 5.3.1-5) 5.3.1 20160101 does not complain
What's wrong with the Ubuntu flavour of g++ 4.8.4?
Do not type-pun. There is no reason for this. Instead properly use memcpy() to copy to and from your int64_t argument. Optimizer will do the rest.

error "cannot appear in a constant-expression" in g++ but not in gcc

98 static inline int set_hw_br(pid_t tracee, dr7_t *pdr7, void *addr, int dr_index)
99 {
100 errno = 0;
101 printf("LINE = %d <pid> %d, dr_index= %d addr=%u \n",__LINE__, tracee, dr_index, addr);
102 //if (ptrace(PTRACE_POKEUSER, tracee, offsetof(struct user, u_debugreg[dr_index]), addr))
103 if (ptrace(PTRACE_POKEUSER, tracee, offsetof(struct user, u_debugreg[dr_index]), addr))
104 {
105 int ii = errno;
106 printf("MKH: 22 errno = %d\n", ii);
107 ptrace(PTRACE_DETACH, tracee, 0, 0);
108 return -1;
109 }
110 else
111 printf("PTRACE_POKEUSER passed...\n");
Above code(part of main code) is successfully compiled in GCC compiler. But while compiling through G++, it is giving fillowing error: error: 'dr_index' cannot appear in a constant-expression in line 103. set_hw_br is called from another function.
Any idea why this failing in g++?
Thanks.
The offsetof macro requires that the member-designator has to produce an address constant (C11 7.19/3):
offsetof(type, member-designator)
which expands to an integer constant expression that has type size_t, the value of
which is the offset in bytes, to the structure member (designated by member-designator),
from the beginning of its structure (designated by type). The type and member designator
shall be such that given
static type t;
then the expression &(t.member-designator) evaluates to an address constant. (If the
specified member is a bit-field, the behavior is undefined.)
In your code, t.u_subreg[dr_index] is not a constant, because dr_index is not a constant.
GCC implements offsetof with a compiler intrinsic so what is allowed in an offsetof expression depends on the rules of GCC's intrinsic. As an extension to the standard, the GCC C front-end allows a non-constant expression as input and produces a non-constant result. The C++ front-end does not allow it, giving the error telling you dr_index cannot be used there.
You can change the offsetof expression to only use constants:
offsetof(struct user, u_debugreg[0])
then you can add the index to it, where T is the type in the array u_debugreg:
offsetof(struct user, u_debugreg[0]) + sizeof(T)*dr_index
(This assumes that u_debugreg is an actual array, not a pointer).

Clang 3.1 and user defined literals

Clang 3.1 claims to support user defined literals. I can define this:
int operator"" _tryit(long double n) { return int(n); }
but when I try to use it I get an error:
int m = 5_tryit;
Invalid suffix '_tryit' on integer constant
5 cannot be implicitly converted to a long double in your case. You need to change it to 5.0 to make it a long double or explicitly invoke the function yourself for the implicit conversion to work:
int m = 5.0_tryit;
OR
int n = operator"" _tryit(5);
(tested both with clang version 3.1 (trunk) (llvm/trunk 155821))
This SO question has a good explanation of the rules.
(Also, as abarnert mentions, make sure you are passing the -std=c++11 flag to the compiler when compiling).