The following snippet works fine in Clang 3.5 but not in GCC 4.9.2:
int main()
{
constexpr volatile int i = 5;
}
with error:
error: both 'volatile' and 'constexpr' cannot be used here
If I inspect the assembly that Clang generates, it shows 5 as expected:
movl $5, -4(%rsp)
In GCC, constexpr int i = 5 is optimized away, but volatile int i = 5 also shows 5 in the assembly. volatile const int i = 5 compiles in both compilers. It's not a foreign concept for something to be both volatile and const at the same time.
Which compiler is correct by the standards?
Yes, this is valid, there was defect report 1688: Volatile constexpr variables that was filed for this, saying:
There does not appear to be language in the current wording stating
that constexpr cannot be applied to a variable of volatile-qualified
type. Also, the wording in 5.19 [expr.const] paragraph 2 referring to
“a non-volatile object defined with constexpr” might lead one to infer
that the combination is permitted but that such a variable cannot
appear in a constant expression. What is the intent?
it was rejected as not a defect(NAD), the response and rationale was:
The combination is intentionally permitted and could be used in some
circumstances to force constant initialization.
As the DR points out such a variable is itself not usable in a constant expression:
constexpr volatile int i = 5;
constexpr int y = i ; // Not valid since i is volatile
Section [expr.const]/2 includes all the cases that makes a conditional-expression not a core constant expression including:
an lvalue-to-rvalue conversion (4.1) unless it is applied to
and all the exception require:
[...]that refers to a non-volatile [...] object [...]
Quoting N4140 [dcl.constexpr]/9:
A constexpr specifier used in an object declaration declares the object as const. Such an object shall have literal type and shall be initialized.
Literal type is defined in [basic.types]/10:
A type is a literal type if it is:
(10.1) — void; or
(10.2) — a scalar type; or
(10.3) — a reference type; or
(10.4) — an array of literal type; or
(10.5) — a class type (Clause 9) that has all of the following properties:
(10.5.1) — it has a trivial destructor,
(10.5.2) — it is an aggregate type (8.5.1) or has at least one constexpr constructor or constructor template that is not a copy or move constructor, and
(10.5.3) — all of its non-static data members and base classes are of non-volatile literal types.
Scalar type is in paragraph 9:
Arithmetic types (3.9.1), enumeration types, pointer types, pointer to member types (3.9.2), std::nullptr_t, and cv-qualified versions of these types (3.9.3) are collectively called scalar types.
int is arithmetic, so volatile int is a scalar type and hence a literal type. constexpr volatile int i = 5; is thus a well-formed declaration.
Interestingly, an expression that evaluates i cannot be a core-constant-expression since it applies an lvalue-to-rvalue conversion to a glvalue of volatile type ([expr.const]/2). Consequently, expressions that evaluate i are neither integral constant expressions nor constant expressions. I'm not sure that the constexpr in that declaration has any effect beyond making i implicitly const, and (nod to #T.C.) requiring its initializer to be a constant expression.
I've reported this as GCC bug 65327, we'll see what the GCC folks have to say.
2015-03-16 Update: Bug has been fixed for GCC 5.
Related
This is a follow-up question is my previous question: Why are member functions returning non-static data members not core constant expressions?
The reduced version of the example mentioned in that question is:
struct S {
const bool x = true;
constexpr bool f() { return x; }
};
int main() {
S s{};
static_assert(s.f()); // error: 's' is not a constexpr;
}
The applicable wording from the standard is N4861: [expr.const]/(5.1):
An expression E is a core constant expression unless the evaluation
of E, following the rules of the abstract machine
([intro.execution]), would evaluate one of the following:
(5.1) this ([expr.prim.this]), except in a constexpr function ([dcl.constexpr]) that is being evaluated as part of E;
As far as I can parse, the expression E is s.f() and it evaluates this since s.f() returns a non-static member this->x. But that falls under the "except" part: the member function s.S::f() is constexpr function that's being evaluated as part of s.f(). If I parsed correctly, I'm expecting s.f() to be constant expression and the assertion success.
However, this bullet doesn't specify a requirement that says that s has to be a constant expression. I can't understand why declaring s as constexpr compiles the program even though there's no requirement, defined in this bullet, for s to be constexpr.
I'm just applying the wording (5.1) in my example but I can't see that constexpr is required here unless I'm missing any other rule.
Because return x; performs lvalue-to-rvalue conversion, the whole kaboodle is not a core constant expression:
An expression E is a core constant expression unless the evaluation of E, following the rules of the abstract machine ([intro.execution]), would evaluate one of the following:
an lvalue-to-rvalue conversion unless it is applied to
a non-volatile glvalue that refers to an object that is usable in constant expressions, or
a non-volatile glvalue of literal type that refers to a non-volatile object whose lifetime began within the evaluation of E;
lvalue-to-rvalue conversion is applied to this->S::x, which is generally forbidden, and neither of the exceptions apply to permit it.
The more relevant exception applies if x (which resolves to this->S::x) is an object that is usable in constant expressions. But it only would be if the struct S object were usable in constant expressions:
a non-mutable subobject or reference member of any of the above.
That requires it to be potentially-constant:
A variable is potentially-constant if it is constexpr or it has reference or const-qualified integral or enumeration type.
A constant-initialized potentially-constant variable is usable in constant expressions at a point P if ...
And S s{}; is not potentially-constant. So it is not usable in constant expressions, and neither are its subobjects.
To answer the title question, this is not a core constant expression, because it is the address of an object with automatic storage duration; that address may change at runtime. This is completely irrelevant for the static_assert in the question code: Being a constant pointer value is neither necessary nor sufficient for a this pointer to be "usable in constant expressions", which in turn is not sufficient for the object found through the pointer to be usable in constant expressions.
This code works:
struct Blob {
static constexpr int a = 10;
};
int main() {
Blob b;
auto c = b.a;
}
But if I change int to float I get an error:
struct Blob {
static constexpr float a = 10.0f;
};
/tmp/main-272d80.o: In function main': main.cpp:(.text+0xe):
undefined reference toBlob::a'
Why can't I use a constexpr float in that way?
Compiler:
Ubuntu clang version 3.5.0-4ubuntu2 (tags/RELEASE_350/final)
Tested on gcc version 4.9.1 (Ubuntu 4.9.1-16ubuntu6) and there were no error.
EDIT:
It will compile if I use -O1, -O2, -O3 or -Os but fails with -O0
C++11 reads
A variable whose name appears as a potentially-evaluated expression is
odr-used unless it is an object that satisfies the requirements for
appearing in a constant expression (5.19) and the lvalue-to-rvalue
conversion (4.1) is immediately applied.
Clearly the l-t-r conversion is immediately applied, and a constexpr variable of floating point type can appear in constant expressions as per [expr.const]/(2.7.1):
A conditional-expression is a core constant expression unless it
involves one of the following as a potentially evaluated subexpression
[..]
an lvalue-to-rvalue conversion (4.1) unless it is applied to
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
Seems to be a Clang bug.
Interestingly, if we use Blob::a instead, clang does not complain:
auto c = Blob::a;
This should not matter for determining if the it is odr-used or not. So this looks like a clang bug which I can reproduce on clang 3.7 using no optimization only. We can tell this is an odr issue since adding a out of class definition fixes the issue (see it live):
constexpr float Blob::a ;
So when do you need to define a static constexpr class member? This is covered in section 9.4.2 [class.static.data] which says (emphasis mine going forward):
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. [ Note: In both
these cases, the member may appear in constant expressions. —end note ] The member shall still be defined
in a namespace scope if it is odr-used (3.2) in the program and the namespace scope definition shall not
contain an initializer.
It requires a definition if it is odr-used. Is it odr-used? No, it is not. The original C++11 wording in section 3.2 [basic.def.odr] says:
An expression is potentially evaluated unless it is an unevaluated operand (Clause 5) or a subexpression
thereof. A variable whose name appears as a potentially-evaluated expression is odr-used unless it is an
object that satisfies the requirements for appearing in a constant expression (5.19) and the lvalue-to-rvalue
conversion (4.1) is immediately applied.
a satisfies both conditions, it is a constant expression and the lvalue-to-rvalue conversion is immediately applied. Defect Report 712 has changed the wording which applies to C++11 since it is a defect report and 3.2 now says:
A variable x whose name appears as a potentially-evaluated expression ex is odr-used unless applying the
lvalue-to-rvalue conversion (4.1) to x yields a constant expression (5.19) that does not invoke any non-trivial
functions and, if x is an object, ex is an element of the set of potential results of an expression e, where
either the lvalue-to-rvalue conversion (4.1) is applied to e, or e is a discarded-value expression
The potential result that matches would be:
If e is an id-expression (5.1.1), the set contains only e.
it is a constant expression and the lvalue-to-rvalue conversion is applied so it is not odr-used.
Can someone clarify why is this legal C++ code? (Yes, I'm asking why my code works ;) )
#include <iostream>
#include <vector>
int main()
{
const std::size_t N = 10;
int a[N]{}; // value-initialize it to get rid of annoying un-initialized warnings in the following line
std::cout << a[5] << std::endl; // got a zero
}
The size of the array is declared as const (NOT constexpr), still the program compiles with no warnings (-Wall, -Wextra, -Wpedantic) in both g++ and clang++. I thought that the C++ standard explicitly specified that the size of the array should be a compile-time constant. It is absolutely not the case here.
N4140 §5.19 [expr.const]/p2, bullet 2.7.1, and p3:
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:
[...]
an lvalue-to-rvalue conversion (4.1) unless it is applied to
a non-volatile glvalue of integral or enumeration type that refers to a non-volatile const object with a preceding initialization,
initialized with a constant expression [ Note: a string literal
(2.14.5) corresponds to an array of such objects. —end note ]
a non-volatile glvalue that refers to a non-volatile object defined with constexpr, or that refers to a non-mutable sub-object of such an object, or
a non-volatile glvalue of literal type that refers to a non-volatile object whose lifetime began
within the evaluation of e;
[...]
3 An integral constant expression is an expression of integral or unscoped enumeration type, implicitly converted to a prvalue, where the converted expression is a core constant expression. [ Note: Such expressions may be used as array bounds (8.3.4, 5.3.4), as bit-field lengths (9.6), as enumerator initializers if the underlying type is not fixed (7.2), and as alignments (7.6.2). —end note ]
In your code, N is a "non-volatile glvalue of integral or enumeration type", it refers to a "non-volatile const object with a preceding initialization", so applying the lvalue-to-rvalue conversion to it does not prevent the expression from being a core constant expression despite the absence of constexpr.
Where did you get that strange idea that N is "absolutely NOT a compile-time constant", as you state in the code comments?
Since the beginning of times, a const integral object declared with an integral constant expression initializer by itself forms an integral constant expression. I.e. is a compile-time constant in C++.
This applies equally to namespace declarations, local declarations and static class member declarations.
(It would not be a compile-time constant in C. But it has always been a compile-time constant in C++.)
Well - N is constant during compilation so it is equivalent to
int a[10]{};
A const int initialized with a literal is considered a constant expression
From N1905 5.19
An integral constant-expression can involve only literals of arithmetic types enumerators, non-volatile const variables or static data members of integral or enumeration types initialized with constant expressions
Note the "non-volatile," indicating your original code should have been rejected by g++.
As int() and int{} are constant expressions of value equal to 0, I thought they are equivalent and interchangeable, thus compilers must treat them equally. For example,
int a[0]; //error: zero-sized array not allowed in ISO C++
int b[int()]; //error: zero-sized array not allowed in ISO C++
int c[int{}]; //error: zero-sized array not allowed in ISO C++
But it seems there are some corner cases where they're not interchangeable.
When initializing a pointer:
int *p = 0; //ok
int *q = int(); //error - by clang only
int *r = int{}; //error - by gcc and clang both
See GCC and Clang messages. I suspect this is a bug in both compilers, as I expect them to be interchangeable in this context, but I would be glad to be proven wrong. :-)
When passing to class template:
template<int N> struct X{};
X<0> x1; //ok
X<int{}> x2; //ok (same as X<0>)
X<int()> x3; //error
See GCC and Clang messages.
I find the syntax X<int()> quite familiar as I've seen (and probably used) the similar syntax before, such as in std::function<int()>, the template argument int() is expected to be function type (instead of 0) taking no argument and returning int. But I want to know the section of the spec which says in this context int() is to be treated as function type and is not equivalent to int{} which is always 0.
The expressions int() and int{} are both constant expression prvalues of integer type that evaluate to zero, and are therefore interchangeable with the literal 0 in any context that requires an integral constant expression prvalue of integer type which evaluates to zero.
Both expressions satisfy the requirements for a constant expression as specified in 5.19 Constant Expressions [expr.const].
Regarding X<int()>, the standard specifies that int() is not interpreted as an expression in this context:
14.3 Template arguments [temp.arg]
In a template-argument, an ambiguity between a type-id and an expression is resolved to a type-id, regardless of the form of the corresponding template-parameter.
Regarding pointer conversions:
4.10 Pointer conversions [conv.ptr]
A null pointer constant is an integral constant expression (5.19) prvalue of integer type that evaluates to zero
or a prvalue of type std::nullptr_t.
Based on the above paragraph, both int() and int{} are null pointer constant expressions. This points to a (very minor) compiler bug, although there is an open Defect Report (903) which may lead to this paragraph changing:
There was a strong consensus among the CWG that only the literal 0 should be considered a null pointer constant, not any arbitrary zero-valued constant expression as is currently specified.
The following wording deals with the value of the expression int():
8.5 Initializers [dcl.init]
To zero-initialize an object or reference of type T means:
[omitted clauses which don't apply]
— if T is a scalar type (3.9), the object is set to the value 0 (zero), taken as an integral constant expression,
converted to T
[...]
To value-initialize an object of type T means:
— if T is a (possibly cv-qualified) class type (Clause 9) with a user-provided constructor (12.1), then the
default constructor for T is called (and the initialization is ill-formed if T has no accessible default
constructor);
[omitted clauses which don't apply]
— otherwise, the object is zero-initialized.
An object whose initializer is an empty set of parentheses, i.e., (), shall be value-initialized.
And for the value of int{}:
8.5.4 List-initialization [dcl.init.list]
List-initialization of an object or reference of type T is defined as follows:
— If the initializer list has no elements and T is a class type with a default constructor, the object is
value-initialized.
[omitted clauses which don't apply]
— Otherwise, if the initializer list has no elements, the object is value-initialized.
All quotes from C++ Working Draft Standard N3337.
I read a little of CLang implementation of standard library and it confuses me a little bit on const and constexpr.
template<class _Tp, _Tp __v>
struct integral_constant
{
static constexpr _Tp value = __v;
};
template<class _Tp, _Tp __v>
const _Tp integral_constant<_Tp, __v>::value;
What makes me confusing is that, it is using constexpr inside class definition and const outside. My question is, is that allowed? And under what situation const and constexpr can be used interchangeably? Of course constexpr functions cannot apply to const, so I am talking about const data and constexpr data.
I did read some standard draft and the proposal in
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2235.pdf,
but it makes me feel more confusing. So I have some more questions,
In N2235, it clearly states that, const data are not guaranteed to be a compile time constants, see the following example,
struct S {
static const int size;
};
const int limit = 2 * S::size; // dynamic initialization
const int S::size = 256;
and constexpr is supposed to solve this, so at least under this situation, constexpr is not allowed as below,
struct S {
static const int size;
};
constexpr int limit = 2 * S::size; // shall be error in my understanding
const int S::size = 256;
However, after reading C++ standard draft N3225, I see nowhere explicitly stated that the above example shall cause an error. Particularly, from 7.1.5/9,
A constexpr specifier used in an
object declaration declares the object
as const. Such an object shall have
literal type and shall be initialized.
If it is initialized by a constructor
call, the constructor shall be a
constexpr constructor and every
argument to the constructor shall be a
constant expression. that call shall
be a constant expression (5.19).
Otherwise, every full-expression that
appears in its initializer shall be a
constant expression.
Therefore, if constexpr int limit = 2 * S::size; is invalid, then S::size must not be an constant expression, then from 5.19 (constant expression), I see nowhere the standard disallow 2 * S::size in the above example to not be a constant expression.
Can anybody point out anything I have overlooked? Thank you very much.
S::size is not a constant expression according to N3225 §5.19p2:
A conditional-expression is a constant expression unless it involves one of the following…
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
[other conditions that don't apply]
Note how the second bullet point I quoted allows an integral static data member which is itself initialized with a constant expression to also be a constant expression, but your S::size is uninitialized.
(Side-note: constant-expressions are defined in terms of conditional-expressions because that's how the C++ grammar works.)
If you're wondering how the lvalue-to-rvalue conversion happens, see §5p9:
Whenever a glvalue expression appears as an operand of an operator that expects a prvalue for that operand, the lvalue-to-rvalue (4.1), array-to-pointer (4.2), or function-to-pointer (4.3) standard conversions are applied to convert the expression to a prvalue.
This is probably a good example of how reading the standard doesn't make a good reference, though there's not much else available yet for 0x.
"every full-expression that appears in it's initializer shall be a constant expression"
S::size is not a constant expression, therefore it can not appear in the initialization of a constant expression.