Why is this not a constexpr? - c++

#include <iostream>
union gc_bits {
size_t value;
struct {
size_t arena : 2;
} bits;
constexpr gc_bits(size_t value_) : value(value_) {
}
};
static constexpr size_t get_max_arenas() {
return gc_bits(~0ULL).bits.arena;
}
size_t current_colour[get_max_arenas()]; // error
int main() {
std::cout << get_max_arenas() << std::endl;
}
The array declaration errors out because get_max_arenas is not a constexpr. I'm not clear on why this should be so.

Slightly rephrasing your program:
static constexpr auto gma = get_max_arenas();
size_t current_colour[gma]; // error
gives the Clang error:
read of member 'bits' of union with active member 'value' is not
allowed in a constant expression
The reason you get this error is that the constructor sets the value, and then you try to read bits. This is not allowed, as commented by #gurka.
Standard quote:
[expr.const]
2 A conditional-expression e is a core constant expression unless the
evaluation of e, following the rules of the abstract machine (1.9),
would evaluate one of the following expressions:
(2.8) — an lvalue-to-rvalue conversion (4.1) or modification (5.18,
5.2.6, 5.3.2) that is applied to a glvalue that refers to a non-active member of a union or a subobject thereof;

Related

Visual Studio C++ compiler weird behaviour

I'm just curious to know why this small piece of code compiles correctly (and without warnings) in Visual Studio. Maybe the result is the same with GCC and Clang, but unfortunately I can't test them now.
struct T {
int t;
T() : t(0) {}
};
int main() {
T(i_do_not_exist);
return 0;
}
T(i_do_not_exist); is an object declaration with the same meaning as T i_do_not_exist;.
N4567 § 6.8[stmt.ambig]p1
There is an ambiguity in the grammar involving expression-statements and declarations: An expression-statement with a function-style explicit type conversion (5.2.3) as its leftmost subexpression can be indistinguishable from a declaration where the first declarator starts with a (. In those cases the statement is a declaration.
§ 8.3[dcl.meaning]p6
In a declaration T D where D has the form
( D1 )
the type of the contained declarator-id is the same as that of the contained declarator-id in the declaration
T D1
Parentheses do not alter the type of the embedded declarator-id, but they can alter the binding of complex declarators.
Because it defines a variable of type T:
http://coliru.stacked-crooked.com/a/d420870b1a6490d7
#include <iostream>
struct T {
int t;
T() : t(0) {}
};
int main() {
T(i_do_not_exist);
i_do_not_exist.t = 120;
std::cout << i_do_not_exist.t;
return 0;
}
The above example looks silly, but this syntax is allowed for a reason.
A better example is:
int func1();
namespace A
{
void func1(int);
struct X {
friend int (::func1)();
};
}
Probably other examples could be found.

Should constexpr initialization happen before other initialization

I have the following piece of code, that behaves as expected on gcc and clang. However, MSVC gives me an unexpected result.
Lets first look at the problematic code.
#include <iostream>
// -----------------------------------------------
class Test // Dummy for MCVE
{
public:
Test();
void Print();
private:
int arr[5];
};
Test tst;
// -----------------------------------------------
template<typename T>
struct range // some stuff not needed by example removed
{
constexpr range(T n) : b(0), e(n) {}
constexpr range(T b, T e) : b(b), e(e) {}
struct iterator
{
T operator*() { return i; }
iterator& operator++() { ++i; return *this; }
bool operator!=(iterator other) { return i != other.i ; }
T i;
};
iterator begin() const { return{ b }; }
iterator end() const { return{ e }; }
private:
T b,e;
};
constexpr range<int> coord(5);
// -----------------------------------------------
Test::Test()
{
for(auto i : coord)
arr[i]=i;
}
void Test::Print()
{
for(auto i : coord)
std::cout << arr[i] << std::endl;
}
// -----------------------------------------------
int main()
{
tst.Print();
}
Now, on both clang and gcc this prints '0 1 2 3 4'
However, on MSVC this prints '0 0 0 0 0'
The reason being that when the constructor on the global variable tst
runs, 'coord' have yet not been initialized (to 0,5), but it is also not random, but rather (0,0).
To me it would make sense that constexpr initialization happens before regular initialization. Is MSVC conformant in this behaviour?
I should perhaps note that I'm using MSVC version 14.0.22823.1, and
that the expected result can be obtained by changing the order of
the declarations
For static storage duration objects initialization must happen in this order:
Zero-initialization.
Constant initialization (i.e. constexpr).
Dynamic initialization.
Relevant standardeese,
C++14 §3.6.2/2:
” Variables with static storage duration (3.7.1) or thread storage duration (3.7.2) shall be zero-initialized (8.5)
before any other initialization takes place. […] Constant initialization is performed: […] if an object with static or thread storage duration is initialized by a constructor call, and if the
initialization full-expression is a constant initializer for the object; […] Together, zero-initialization and constant initialization are called static initialization; all other initialization is dynamic initialization. Static initialization shall be performed before any dynamic initialization takes place.
The same paragraph defines (breaking the flow of text, so I removed it above)
” A constant initializer for an object o is an expression that is a constant expression, except that it may also invoke constexpr constructors for o and its subobjects even if those objects are of non-literal class types.
In the reported example, which I've verified with Visual C++ 2015, the dynamic initialization of the static storage duration object tst takes place before the constant initialization of the static storage duration object coord, and that's a compiler bug.

typeid doesn't work with non-static member function

clang doesn't compile the third call to typeid below (see live example). But I can't see anything in §5.2.8 that disallows this, specially when we consider that the expression B::f is not a glvalue of polymorphic class type (see paragraph 3). Also, according to this paragraph the expression B::f is an unevaluated operand, and as such, the call typeid(B::f) should compile. Note that GCC doesn't compile any of the calls to typeid below:
#include <iostream>
#include <typeinfo>
struct A{ int i; };
struct B{ int i; void f(); };
int main()
{
std::cout << typeid(A::i).name() << '\n';
std::cout << typeid(B::i).name() << '\n';
std::cout << typeid(B::f).name() << '\n';
}
As far as I can tell clang is correct, using a non static member is only valid in an unevaluated context if it is a data member. So it looks like gcc is incorrect for the first two cases, but gcc works correctly in the case of sizeof and decltype which also have unevaluated operands.
From the draft C++11 standard section 5.1.1 [expr.prim.general]:
An id-expression that denotes a non-static data member or non-static
member function of a class can only be used:
and includes the following bullet:
if that id-expression denotes a non-static data member and it appears
in an unevaluated operand. [ Example:
struct S {
int m;
};
int i = sizeof(S::m); // OK
int j = sizeof(S::m + 42); // OK
—end example ]
The rest of the bullets do not apply, they are as follows:
as part of a class member access (5.2.5) in which the object expression refers to the member’s class61 or a class derived from that
class, or
to form a pointer to member (5.3.1), or
in a mem-initializer for a constructor for that class or for a class derived from that class (12.6.2), or
in a brace-or-equal-initializer for a non-static data member of that class or of a class derived from that class (12.6.2), or
We know that operand is unevaluated from section 5.2.8 which says:
When typeid is applied to an expression other than a glvalue of a
polymorphic class type, [...] The expression is an unevaluated operand
(Clause 5).
We can see from the grammar that an id-expression is either an unqualified-id or a qualified-id:
id-expression:
unqualified-id
qualified-id
Update
Filed a gcc bug report: typeid does not allow an id-expression that denotes a non-static data member.
typeid(A::i).name() doesn't quite do what I thought it would do. I expected it to be a pointer-to-member, but it's actually just an int.
To see this, run this code:
#include <iostream>
struct A{ int i; };
struct B{ int i; void f(void); };
template<typename T>
void what_is_my_type() {
std:: cout << __PRETTY_FUNCTION__ << std:: endl;
}
int main()
{
what_is_my_type<decltype(&A::i)>(); // "void what_is_my_type() [T = int A::*]"
what_is_my_type<decltype(&B::i)>(); // "void what_is_my_type() [T = int B::*]"
what_is_my_type<decltype(&B::f)>(); // "void what_is_my_type() [T = void (B::*)()]"
what_is_my_type<decltype(A::i)>(); // "void what_is_my_type() [T = int]"
what_is_my_type<decltype(B::i)>(); // "void what_is_my_type() [T = int]"
// what_is_my_type<decltype(B::f)>(); // doesn't compile
}
I've put the output in a comment after each call.
The first three calls work as expected - all three work and the type information includes the type of the struct (A or B) as well as the type of member.
The last three are different though. The final one doesn't even compile, and the first two simply print int. I think this is a clue as to what is wrong. It is possible, given a particular A or B, to take the address of that particular member:
A a;
int * x = &(a.i);
*x = 32;
but it is not possible (or even meaningful?) to do this:
B b;
??? y = &(a.f); // what does this even mean?
Finally, to emphasize that this is not about pointers, consider this:
A a;
B b;
int x = a.i;
int y = b.i;
??? z = b.f; // what would this mean? What's its type?

Compile-time array constants

I seem to be missing something rather fundamental.
I'm trying to use const array members at compile-time.
const int list[3] = { 2, 5, 7 };
const int a = list[2]; // this doesn't error?
template<int N1, int N2>
struct tmax {
enum { value = ((N1 > N2) ? N1 : N2) };
};
const int b = tmax<2,4>::value;
const int c = tmax<list[0],list[1]>::value; // error is here
int main()
{
return 0;
}
Errors:
prog.cpp:10:24: error: 'list' cannot appear in a constant-expression
prog.cpp:10:30: error: an array reference cannot appear in a constant-expression
Here is the relevent IDEOne link
So why doesn't this work? What am I missing? What should I do differently?
Just because an object is const doesn't mean it's a compile time constant expression.
main.cpp:10:20: error: non-type template argument is not a constant expression
const int c = tmax<list[0],list[1]>::value; // error is here
^~~~~~~
main.cpp:10:20: note: read of non-constexpr variable 'list' is not allowed in a constant expression
main.cpp:1:11: note: declared here
const int list[3] = { 2, 5, 7 };
^
This is the reason for constexpr:
constexpr int list[3] = { 2, 5, 7 };
template<int N1, int N2>
struct tmax {
enum { value = ((N1 > N2) ? N1 : N2) };
};
const int b = tmax<2,4>::value;
const int c = tmax<list[0],list[1]>::value; // works fine now
As for why this works:
const int a = list[2]; // this doesn't error?
initializing a const variable doesn't require a constant expression:
int foo(int n) {
const int a = n; // initializing const var with a non-compile time constant
Expressions are not constant expressions if they contain any one of a number of disallowed sub-expressions. One such class of disallowed sub-expressions is:
an lvalue-to-rvalue conversion (4.1) unless it is applied to
a glvalue of integral or enumeration type that refers to a non-volatile const object with a preceding
initialization, initialized with a constant expression, or
a glvalue of literal type that refers to a non-volatile object defined with constexpr, or that refers
to a sub-object of such an object, or
a glvalue of literal type that refers to a non-volatile temporary object whose lifetime has not
ended, initialized with a constant expression;
In particular, while the name of a const object of enum or intergral type initialized with a constant initializer forms a constant expression (reading its value is what causes the lvalue-to-rvalue conversion), sub-objects of an const aggregate object (such as list in your example, an array) do not, but would if declared constexpr.
const int list[3] = { 2, 5, 7 };
const int a = list[2];
This is valid but a does not constitute a constant expression because it is not initialized with a constant expression.
By changing the declaration of list (we don't have to change the declaration of a), we can make a form a constant expression.
constexpr int list[3] = { 2, 5, 7 };
const int a = list[2];
As list[2] is now a constant expression, a is now a const object of intergral type initialized with a constant expression so a can now be used as a constant expression.

static_cast dynamic_cast: expected constant expression?

In visual C++ 2010, when compiling the following codes, I get error message:
static_cast error C2057: expected constant expression.
what's wrong with that?
struct A {};
struct B : A {};
struct XX
{
static const int offset = (long)static_cast<A*>((B*)0x8) - 0x8;
};
Thanks AProgrammer, the following is correct for VC 2010:
struct A {};
struct B : A {};
struct XX
{
static const int offset;
};
const int XX::offset
= (long)static_cast<A const*>((B const*)0x8) - 0x8;
Your casts to A* and B* prevent the initializer of x to be a constant expression:
5.19/3
Cast operators in an arithmetic constant expression shall only convert arithmetic or enumeration types to arithmetic or enumeration types, excepted as part of an operand to the sizeof operator.
which is needed in that context:
9.2/4
A member-declarator can contain a constant-initializer only if it declares a static member of integral or enumeration type.
Why not just say:
static const int x =0x8;