问题
#include <iostream>
constexpr int func2(int const& id){
return id;
}
template<int v>
struct Test{
};
int main(){
const int v = 0;
Test<func2(v)> c;
}
Consider the above code,I just don't understand why the code is well-formed.My pointview is that the name v
is used as a glvalue when evalute expression func2
,becuase the parameter of func2
is of reference type,the v
need to be bound to the id-expression id
.So we look at the requirement of a glvalue constant expression,here are quotes about that.
A constant expression is either a glvalue core constant expression that refers to an entity that is a permitted result of a constant expression (as defined below), or a prvalue core constant expression whose value satisfies the following constraints.
We ignore the case of prvalue,because here v
is used as a glvalue.
An entity is a permitted result of a constant expression is:
An entity is a permitted result of a constant expression if it is an object with static storage duration that is either not a temporary object or is a temporary object whose value satisfies the above constraints, or it is a function.
In my program portion,The const int v = 0;
does not have static storage duration,it just has automatic storage duration.So when evaluting the expression func2(v)
to determine whether it is a constant expression,Firstly,the v
must be a glvalue core constant expression that refers to an entity that is a permitted result of a constant expression,therefore,why the program is well-formed here?If I lose any important quote,Please correct me.
回答1:
We ignore the case of prvalue, because here v is used as a glvalue
Is it though? This is an example from cppreference:
void test() {
static const int a = std::random_device{}();
constexpr const int& ra = a; // OK: a is a glvalue constant expression
constexpr int ia = a; // Error: a is not a prvalue constant expression
const int b = 42;
constexpr const int& rb = b; // Error: b is not a glvalue constant expression
constexpr int ib = b; // OK: b is a prvalue constant expression
}
And yes, const int b = 42
is rather weird here because technically speaking, you can bind b
to const int&
, const_cast
the const
away and assign a runtime value to it. However, considering what is an integral constant expression and what are the requirements of a const object it makes perfect sense:
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. If an expression of class type is used where an integral constant expression is expected, the expression is contextually implicitly converted to an integral or unscoped enumeration type.
Variable b
sure does look like something you could implicitly convert to a prvalue constant expression because it basically serves as an alias for literal 42
in this context and integer literals are prvalues by definition.
Now for the problematic part - this:
const object - an object whose type is const-qualified, or a non-mutable subobject of a const object. Such object cannot be modified: attempt to do so directly is a compile-time error, and attempt to do so indirectly (e.g., by modifying the const object through a reference or pointer to non-const type) results in undefined behavior.
And:
A core constant expression is any expression whose evaluation would not evaluate any one of the following:
...
an expression whose evaluation leads to any form of core language undefined behavior (including signed integer overflow, division by zero, pointer arithmetic outside array bounds, etc). Whether standard library undefined behavior is detected is unspecified.
Means that as soon as you start doing funny things with that b
, you can expect anything to happen. For example, this is what I tried doing to your code in latest MSVC with all standard-conformance options switched on:
#include <iostream>
#include <random>
constexpr int func2(int const& id) {
return id;
}
template<int v>
struct Test {
long array[v];
};
int main() {
const int v = 0;
const int& ref = v;
const_cast<int&>(ref) = std::random_device()() % std::numeric_limits<int>::max();
Test<func2(v)> c;
return 0;
}
With language extensions turned on I got a C4200: nonstandard extension used : zero-sized array in struct/union warning. After switching them off, the program wouldn't compile. And when I deleted the array
part from the struct it started compiling again.
回答2:
I try to answer this question.why the func2(v)
is a constant expression,Becuase For expression func2(v),when evaluting this postfix-expression,there's no requirement that v
must be a glvalue constant expression in the list of "would evaluate one of the following expressions:",Even,these rules does not mandate that the one expression within a potentially core constant expression would be a glvalue constant expression,only require the expression does not violate the listed requirement.So let's continue,When initialization of the parameter,It's another rule here:
A full-expression is:
- [...]
- an init-declarator or a mem-initializer, including the constituent expressions of the initializer
So,when evalute this full-expression,it only does not violate these listed condition,then the func2(v)
would be evaluted as a constant expression,So let's look at these rules:
an id-expression that refers to a variable or data member of reference type unless the reference has a preceding initialization and either
- it is initialized with a constant expression or,
- its lifetime began within the evaluation of e;
For id-expression id
,its preceding initialization is the corresponding argument,Because of this rule:
When a function is called, each parameter ([dcl.fct]) shall be initialized ([dcl.init], [class.copy], [class.ctor]) with its corresponding argument.
So,the first condition is true.And "it is initialized with a constant expression" is false,the condition "its lifetime began within the evaluation of e" is true.In conlusion,the expression func2(v)
indeed a constant expression
来源:https://stackoverflow.com/questions/61926165/the-question-about-a-glvalue-constant-expression-used-in-a-context-that-requires