mixing use of constexpr and const? - c++

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.

Related

How to make `this` pointer constant expression?

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.

Why this expression is not constant expression [duplicate]

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.

static_assert evaluates non constant expression

Why does it working?
#include <cstdio>
template<auto x> struct constant {
constexpr operator auto() { return x; }
};
constant<true> true_;
static constexpr const bool true__ = true;
template<auto tag> struct registryv2 {
// not constexpr
static auto push() {
fprintf(stderr, "%s\n", __PRETTY_FUNCTION__);
//*
return true_; // compiles
/*/
return true__; // read of non-const variable 'x' is not allowed in a constant expression
//*/
}
// not constexpr either
static inline auto x = push();
};
static_assert(registryv2<0>::x);
https://godbolt.org/z/GYTdE3M9q
static_assert evaluates non constant expression
Nope, it most certainly does not. Constant evaluation has a strict set of conditions to is must obey in order to succeed.
For starters:
[dcl.dcl]
6 In a static_assert-declaration, the constant-expression shall be a contextually converted constant expression of type bool.
"contextually converted" is standard lingo for "we'll consider explicit conversion operators". The place where it may become counter-intuitive is when "converted constant expression" is defined.
[expr.const]
4 A converted constant expression of type T is an expression, implicitly converted to type T, where the converted expression is a constant expression and the implicit conversion sequence contains only
user-defined conversions,
[...]
The fine point is in the first sentence of the paragraph. The converted expression must be a constant expression. But the source expression doesn't have to be! So long as the conversion sequence is limited to the list in the paragraph and is valid constant evaluation itself, we are in the clear. In your example, the expression registryv2<0>::x has type constant<true>, it can be contextually converted to a bool via the user defined conversion operator. And well, the conversion operator satisfiers all the requirements of a constexpr function and constant evaluation.
The list of requirements for constant evaluation is rather long so I won't go over it to verify every bullet is upheld. But I will demonstrate that we can trip one of them.
template<auto x> struct constant {
bool const x_ = x;
constexpr explicit operator auto() const { return x_; }
};
This change immediately makes the godbolt code sample ill-formed. Why? Because we are doing an lvalue-to-rvalue conversion (standard lingo for access) on a bool when it is not permitted.
An expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine, would evaluate one of the following expressions:
an lvalue-to-rvalue conversion unless it is applied to
a non-volatile glvalue of integral or enumeration type that refers to a complete non-volatile const object with a preceding initialization, initialized with a constant expression, or
a non-volatile glvalue that refers to a subobject of a string literal, or
a non-volatile glvalue that refers to a non-volatile object defined with constexpr, or that refers to a non-mutable subobject 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;
Going over the exceptions, none apply. So now registryv2<0>::x is not a contextually converted constant expression of type bool.
This also explain why true__1 is verboten. Same issue, access to an object that is disallowed.
1 - That's a reserved identifier. Two consecutive underscores belong to the implementation for any use. Not critical to the issue at hand, but take note.
It works because this conversion to auto operator is constexpr.
template<auto x> struct constant {
constexpr explicit operator auto() const { return x; }
};
It is called by the static_assert, even if marked explicit. It looks like putting an expession within a static_assert is akin to making an explicit cast to bool. The same is true for 'if' as in if (registryv2<0>::x)

Understanding constant expression

I'm trying to understand the constant expression concept (from c++reference):
struct S {
static const int c;
};
const int d = 10 * S::c; // not a constant expression: S::c has no preceding
// initializer, this initialization happens after const
const int S::c = 5; // constant initialization, guaranteed to happen first
Why isn't the S::c a constant expression untill we define it. It was declared as a static const data member though...
Quoting relevant part of the C++11 standard (draft N3337), section 5.19, paragraph 2:
A conditional-expression is a core constant expression unless it involves one of the following as a potentially evaluated subexpression (3.2), but subexpressions of logical AND (5.14), logical OR (5.15), and conditional (5.16) operations that are not evaluated are not considered [ Note: An overloaded operator invokes a function. — end note ]:
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
There is no preceding initialization of S::c in your definition of d.
Edit: why this applies:
5.1.1/8: S::c is an lvalue.
3.10/1: a glvalue is an lvalue or xvalue.
5/8: specifies that lvalue-to-rvalue conversion happens whenever an operator that expects a prvalue is used.
Proving that multiplication expects a prvalue is hurting my head. It is implied in many places, but I haven't found anywhere it is said explicitly.
In this sequence …
constexpr int d = 10 * S::c;
const int S::c = 5;
… the value of S::c is not known yet when the d value is compiled. But try to swap these lines:
const int S::c = 5;
constexpr int d = 10 * S::c;
Constant initialization is performed before other initialization in the C++ compiling process. In the example, the constant initialization of d is guaranteed to occur before the constant initialization of S::c. A constant expression must consist exclusively of constant values. When d is initialized, S::c is known to be a constant value, so the expression is not considered constant.

Can constexpr be combined with volatile?

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.