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;
Related
struct X {
int f(int);
static int f(long);
};
int (X::*p1)(int) = &X::f; // OK
int (*p2)(int) = &X::f; // error: mismatch
int (*p3)(long) = &X::f; // OK
int (X::*p4)(long) = &X::f; // error: mismatch
int (X::*p5)(int) = &(X::f); // error: wrong syntax for pointer to member
int (*p6)(long) = &(X::f); // OK
I think that p1 and p5 is the same case. Why is p5 wrong?
Because the standard says so. From the N3936:
5.3.1 Unary operators
A pointer to member is only formed when an explicit & is used and its operand is a qualified-id not enclosed in parentheses. [ Note:
that is, the expression &(qualified-id), where the qualified-id is
enclosed in parentheses, does not form an expression of type “pointer
to member.” Neither does qualified-id, because there is no implicit
conversion from a qualified-id for a non-static member function to the
type “pointer to member function” as there is from an lvalue of
function type to the type “pointer to function” (4.3). Nor is
&unqualified-id a pointer to member, even within the scope of the
unqualified-id’s class. — end note ]
The C++ Standard's definition of the built-in operator & states that only when the parameter to & is a qualified-id, meaning something like Class::Member, does & result in a pointer-to-member. The parentheses make it no longer a qualified-id, so it attempts to parse X::f directly, which is illegal in this context: you're assigning an int (*)(long) to an int (X::*)(int).
The distinction between the two cases resolves an ambiguity. Let's say that you have:
struct X {
int m;
};
struct Y {
int m;
};
struct Z : X, Y {
void F();
};
void Z::F() {
int X::*p1 = &X::m;
int *p2 = &(X::m);
}
Here, &X::m is a pointer-to-member, whereas &(X::m) is an ordinary pointer to int, using the X:: qualification to resolve the ambiguity between X's m and Y's m.
I have this header:
class A{
const int x;
typedef std::array<MyClass, x> ARRAY; // Cannot use x here?
};
and in the implementation file:
A::A() : x(10) {}
but I get compiler errors for the typedef line saying:
invalid use of non-static data member A::x
I thought x only had to be const for use in the array sizing? I really wish to avoid static.
In order to use x as a non-type template parameter, it has to be a core constant expression - basically it has to be evaluatable at compile-time. A simple const is not sufficient criteria, const just means it is not modifiable in the future - it does not mean that it is a known quantity at compile-time.
There is one edge case here which may be causing some confusion in that a const integral is a core constant expression in cases like this:
const int x = 10;
std::array<int, x> arr; // ok
There's no reason to want to avoid static. You will want to do something like this:
struct A {
static constexpr int x = 10;
typedef std::array<MyClass, x> ARRAY;
};
#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;
Last time I am puzzled with the initialization of static const int ,const int and static int , then some kind men help me. But question comes again,
I try these int C_free 5.0
class Q{
static const double qs = 10;//float is also ok
};
Then I try it in Qt creator 2.8.1 ,this can't pass .
So for understand this completely, Can anyone give me a whole explanation, under which situation which type is allowed ? not just int type. Thanks.
Forgive me , new C++ learner , not good English
In
using T = sometype;
class Q
{
static const T qs = somevalue;
};
… T must be an integral type or an enumeration type:
C++11 §9.4.2/3
”
If a non-volatile const static data member is of integral or enumeration type, its declaration in the class
definition can specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression.
The meaning of constant expression here is essentially that it can be evaluated at compile time; it is defined by C++11 §5.19/3.
C++11 constexpr allows a much wider ranger of types, if all relevant compilers support it.
A simple alternative is to do this:
class Q
{
static
auto qs() -> double { return somevalue; }
};
And a more elaborate alternative, the templated const trick:
template< class Dummy_ >
struct Q_constants_
{
static double const qs;
};
template< class Dummy_ >
double Q_constants<Dummy_>::qs = somevalue;
class Q
: public Q_constants_<void>
{};
This is what the draft standard has to say about initialization of const static member data:
9.4.2/3 If a non-volatile const static data member is of integral or enumeration type, its declaration in the class definition can specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression (5.19). A static data member of literal type can be declared in the class definition with the constexpr specifier; if so, its declaration shall specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression.
You have the choice of changing the type of qs to an integral type or change it to a constexpr instead of const.
Use:
class Q
{
static const int qs = 10;
};
or
class Q
{
static constexpr double qs = 10;
};
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.