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.