问题
I have two snippets.
The first snippet:
#include <string>
template <typename T>
constexpr bool foo(T&&) {
return false;
}
int main() {
std::string a;
if constexpr (foo(a)) {
}
}
The second snippet:
#include <string>
template <typename T>
constexpr bool foo(T&&) {
return false;
}
int main() {
std::string a;
std::string& x = a;
if constexpr (foo(x)) {
}
}
The first one compiles, but the second one does not compile (error message: error: the value of ‘x’ is not usable in a constant expression. Why? Why a
is usable in a constant expression and x
is not?
The command, used to compile g++ -std=c++17 main.cpp
.
回答1:
Because usually a constant expression cannot evaluate a reference that refers to an object with automatic storage duration. Here I mean "evaluate" by determining the identity of the object, not by determining the value of the object. So even the value of the object a
is not required in your example (i.e. no lvalue-to-rvalue conversion is applied), foo(x)
is still not a constant expression.
Note foo(a)
does not evaluate any reference. Although the parameter of foo
is a reference, it is not evaluated as an expression. In fact, even if it was evaluated, for example,
template <typename T>
constexpr bool foo(T&& t) {
t;
return false;
}
foo(a)
is still a constant expression. Such cases are exceptions as the reference t
is initialized within the evaluation of foo(a)
.
Related part in the standard (irrelevant part is elided by me):
[expr.const]/2:
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 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;
...
[expr.const]/6:
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), ... 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.
回答2:
Because you can't know at compile time what value x
will have, all you know is that it will point to a
. What you are doing is checking the "value" of x
, but the value of x
is the address where a
is and you can't know where a
will be allocated nor an address is constant.
On the other hand you already know the value of a
, it's an empty std::string.
This question contains some more details: how to initialize a constexpr reference
回答3:
Firstly, In your both code sections, the evaluation context all require the postfix-expression to be a constant expression. The definition of function template foo
satisfies the requirements of a constexpr function
due to its parameter is of literal type.
id-expression a
and x
are all glvalue which evaluation determine the identity of an object, but the only difference is that, In your fist code. copy-initialize the parameter from a
only require identity conversion, because of the following rules:
When a parameter of reference type binds directly to an argument expression, the implicit conversion sequence is the identity conversion, unless the argument expression has a type that is a derived class of the parameter type, in which case the implicit conversion sequence is a derived-to-base Conversion
And it does nothing, due to
[over.ics.scs#2]
As described in Clause [conv], a standard conversion sequence is either the Identity conversion by itself (that is, no conversion)
And the remain expressions are all constant expressions. So, for foo(a)
this expression, it's a constant expression.
For foo(x)
, because x
is an id-expression of reference type that subject to this rule:
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;
The lifetime of x
began before the evaluation of foo(x)
and it isn't initialized with a constant expression because std::string a;
is not a glvalue constant expression. If you modify the std::string a;
to static std::string a;
,then it will be ok.
来源:https://stackoverflow.com/questions/51456712/why-references-cant-be-used-with-compile-time-functions