Why references can't be used with compile time functions?

左心房为你撑大大i 提交于 2021-02-08 05:30:10

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!