On Fri, Mar 1, 2019 at 12:46 PM Alberto Barbati
<
alberto...@gmail.com> wrote:
>
> Il giorno venerdì 1 marzo 2019 12:35:51 UTC+1, Tadeus Prastowo ha scritto:
>>
>> @Jens Maurer: Kindly see the body of this e-mail.
>>
>> On Fri, Mar 1, 2019 at 12:02 PM Alberto Barbati
>> <
alberto...@gmail.com> wrote:
>> >
>>
>> > I believe paragraph [expr.const]/2.7.1 in N4659 might by relevant.
>>
>> But that is an exception given to an lvalue-to-rvalue conversion.
>> Since "&x" performs no lvalue-to-rvalue conversion, logically whether
>> "static const double x = 2" is or is not equal to "static constexpr
>> double x = 2" cannot be decided using that paragraph, can it?
>
>
> &x doesn't perform lvalue-to-rvalue conversion, but the expression *x * 2 does it.
I have scoured the C++17 standard document (
https://wg21.link/n4659)
for hours to find the statement that the expression *x * 2 is required
to perform an lvalue-to-rvalue conversion. Fortunately, I can find
the requirement as can be seen from the following steps:
1. The id-expression `x' is a non-type template-parameter of type
"pointer to const double". So, [temp.param]p6 says: A non-type
non-reference template-parameter is a prvalue.
The type of the prvalue is "pointer to const double" according to
[expr.prim.id.unqual]p1, which says: An identifier is an id-expression
provided it has been suitably declared (Clause 10). [...] The type
of the expression is the type of the identifier.
2. There is a unary operator `*'. So, [expr.unary.op]p1 says: The
unary * operator performs indirection: the expression to which it is
applied shall be a pointer to an object type, or a pointer to a
function type and the result is an lvalue referring to the object or
function to which the expression points. If the type of the
expression is “pointer to T”, the type of the result is “T”.
So, the subexpression `*x' is an lvalue without any conversion
involved; it is the result of an indirection operation. The type of
the lvalue is "const double".
3. Now [basic.fundamental]p8 says: There are three floating-point
types: float, double, and long double. [...] Integral and floating
types are collectively called arithmetic types.
Additionally, [basic.type.qualifier]p1 says: A type mentioned in 6.9.1
and 6.9.2 is a cv-unqualified type. Each type which is a
cv-unqualified complete or incomplete object type or is void (6.9) has
three corresponding cv-qualified versions of its type: a
const-qualified version, a volatile-qualified version, and a
const-volatile-qualified version. [...] The cv-qualified or
cv-unqualified versions of a type are distinct types; however, they
shall have the same representation and alignment requirements (6.11).
So, the type of the lvalue is a const-qualified arithmetic type.
Remember that [basic.type.qualifier]p1 says that "double" and "const
double" are distinct types.
4. There is the binary multiplicative operator `*'. So, [expr.mul]p2
says: The operands of * and / shall have arithmetic or unscoped
enumeration type; the operands of % shall have integral or unscoped
enumeration type. The usual arithmetic conversions are performed on
the operands and determine the type of the result.
5. There is a requirement for the usual arithmetic conversions to be
performed on the operands. So, [expr]p11 says: Many binary operators
that expect operands of arithmetic or enumeration type cause
conversions and yield result types in a similar way. The purpose is to
yield a common type, which is also the type of the result. This
pattern is called the usual arithmetic conversions, which are defined
as follows: [...] — Otherwise, if either operand is double, the other
shall be converted to double.
This requirement, however, cannot be executed at this point, in
particular because the left operand has type "const double", not a
type discussed in [expr]p11 considering point 3 above. To be
coherent, it must be done at point 11 below.
6. Now [over.match.oper]p1 says: If no operand of an operator in an
expression has a type that is a class or an enumeration, the operator
is assumed to be a built-in operator and interpreted according to
Clause 8.
Clause 8 says in [expr.mul]p3: The binary * operator indicates multiplication.
7. Now [over.match.oper]p3 says: For a unary operator @ with an
operand of a type whose cv-unqualified version is T1, and for a binary
operator @ with a left operand of a type whose cv-unqualified version
is T1 and a right operand of a type whose cv-unqualified version is
T2, three sets of candidate functions, designated member candidates,
non-member candidates and built-in candidates, are constructed as
follows:
So, for the subexpression `*x' as the left operand for a binary
operator @ where @ is *, T1 is "double". For the subexpression `2' as
the right operand for the same binary operator, T2 is "double" due to
the usual arithmetic conversion.
Recall that the left operand is lvalue (the right operand is a prvalue
as per [lex.icon]p1 and [expr.prim.literal]p1).
8. Continuing with [over.match.oper]p3, it says:
— If T1 is a complete class type or a class currently being defined,
the set of member candidates is the result of the qualified lookup of
T1::operator@ (16.3.1.1.1); otherwise, the set of member candidates is
empty.
Since T1 is "double", the set of member candidates is empty.
9. Continuing with [over.match.oper]p3, it says:
— The set of non-member candidates is the result of the unqualified
lookup of operator@ in the context of the expression according to the
usual rules for name lookup in unqualified function calls (6.4.2)
except that all member functions are ignored. However, if no operand
has a class type, only those non-member functions in the lookup set
that have a first parameter of type T1 or “reference to cv T1”, when
T1 is an enumeration type, or (if there is a right operand) a second
parameter of type T2 or “reference to cv T2”, when T2 is an
enumeration type, are candidate functions.
Since the program declares no operator*, the set of non-member
candidates is also empty.
10. Continuing with [over.match.oper]p3, it says:
— For the operator ,, the unary operator &, or the operator ->, the
built-in candidates set is empty. For all other operators, the
built-in candidates include all of the candidate operator functions
defined in 16.6 that, compared to the given operator,
— have the same operator name, and
— accept the same number of operands, and
— accept operand types to which the given operand or operands can
be converted according to 16.3.3.1, and
— do not have the same parameter-type-list as any non-member
candidate that is not a function template specialization.
11. Now 16.6 is [over.built]. There are two relevant paragraphs, p2
and p13, which say: In this subclause, the term promoted integral type
is used to refer to those integral types which are preserved by
integral promotion (7.6) (including e.g. int and long but excluding
e.g. char). Similarly, the term promoted arithmetic type refers to
floating types plus promoted integral types. [...] For every pair of
promoted arithmetic types L and R , there exist candidate operator
functions of the form LR operator*(L, R); where LR is the result of
the usual arithmetic conversions between types L and R.
So, the set of built-in candidates includes among other elements:
double operator*(double, int).
Note that based on point 3 and 4 above, all elements in the set have
L, R, and LR as cv-unqualified types.
12. Going back to point 10 above, 16.3.3.1 is [over.best.ics] whose
p1, p2, and, more importantly, p6 say: An implicit conversion sequence
is a sequence of conversions used to convert an argument in a function
call to the type of the corresponding parameter of the function being
called. The sequence of conversions is an implicit conversion as
defined in Clause 7, which means it is governed by the rules for
initialization of an object or reference by a single expression (11.6,
11.6.3). Implicit conversion sequences are concerned only with the
type, cv-qualification, and value category of the argument and how
these are converted to match the corresponding properties of the
parameter. Other properties, such as the lifetime, storage class,
alignment, accessibility of the argument, whether the argument is a
bit-field, and whether a function is deleted (11.4.3), are ignored.
So, although an implicit conversion sequence can be defined for a
given argument-parameter pair, the conversion from the argument to the
parameter might still be ill-formed in the final analysis. [...]
When the parameter type is not a reference, the implicit conversion
sequence models a copy-initialization of the parameter from the
argument expression. The implicit conversion sequence is the one
required to convert the argument expression to a prvalue of the type
of the parameter.
Since "double" is not a reference, "the implicit conversion sequence
is the one required to convert [`*x'] to a prvalue of [type double]".
Recall that `*x' at point 2 above is an lvalue. So, [over.best.ics]p6
is the stipulation that requires an lvalue-to-rvalue conversion, which
you already pointed out.
> In fact clang reports this error:
>
> <source>:3:27: error: constexpr variable 'value' must be initialized by a constant expression
>
> static constexpr double value = *x * 2;
Yes, and in doing so, Clang is correct as per my analysis above.
Thank you very much for your kind help.
I have filed a bug report to GCC at
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89553.
> Alberto
Have a nice weekend!
--
Best regards,
Tadeus