Yes, the conversion from prvalue S{} to type const S& would bind directly, but it "is said to bind directly to the initializer expression", and the initializer expression "S{}" is not a glvalue. A bit confusing, since the reference "binds to" the lvalue resulting from the temporary materialization conversion. But I think this is intended, because I think the point is that a temporary materialization conversion is never applied to just one of the second or third operands of the conditional. If it did, automatically destroying the temporary at the end of its lifetime would depend on more than just the program counter register, causing brand new complications for compilers, particularly for stack unwinding on exception propagation.

There certainly is an implicit conversion sequence from an lvalue of type const S to type S, via the copy constructor. Otherwise we couldn't do:

struct S {};
void f(S) {}
int main() {
    const S s;
    f(s);
}

But in converting "S{}" to a type related to "s", wouldn't [expr.cond]/(4.3) apply, if the conversion in [expr.cond]/(4.1) is not possible? Note there have been changes to (4.3) since C++17:

C++17 N4659:
(4.3.1) if T1 and T2 are the same class type (ignoring cv-qualification), or one is a base class of the other, and T2 is at least as cv-qualified as T1, the target type is T2,
(4.3.2) otherwise, ...

C++20 latest at https://timsong-cpp.github.io/cppwp/expr.cond :
(4.3.1) if T1 and T2 are the same class type (ignoring cv-qualification) and T2 is at least as cv-qualified as T1, the target type is T2,
(4.3.2) otherwise, if T2 is a base class of T1, the target type is cv1 T2, where cv1 denotes the cv-qualifiers of T1,
(4.3.3) otherwise, ...

But I don't see how that change would make any implicit conversion sequence unavailable. Which would mean both are possible and "b ? S{} : s" is ill-formed - not desirable. (It might make sense to say if both are possible and the target types are the same, the expression is a prvalue with that type; but it doesn't say that.)




On Mon, Sep 23, 2019 at 6:51 PM Krystian Stasiowski via Std-Discussion <std-discussion@lists.isocpp.org> wrote:
Only the last case where the initializer expression is converted does it not bind directly  http://eel.is/c++draft/dcl.init.ref#5.4

The implicit conversion sequence can be formed from the prvalue to the lvalue. Since constness matters for prvalues of class type and is not ignored, an implicit conversion sequence cannot be formed from const S to S. 

I think.

On Mon, Sep 23, 2019 at 11:02 AM Brian Bi via Std-Discussion <std-discussion@lists.isocpp.org> wrote:
Consider the following:

#include <type_traits>
struct S {};
bool b;
int main() {
    const S s {};
    static_assert(std::is_same<decltype(b ? S{} : s), const S>::value);
}


Here GCC and Clang confirm that the type is correct. But I can't figure out why. My reading of the standard is that the result should be a prvalue of type S, not const S.

[expr.cond]/4 applies because the two operand types are not the same (one is S, the other is const S) but at least one is a class type. We must therefore try to form an implicit conversion sequence in each direction. An implicit conversion sequence cannot be formed from the prvalue operand to const S& because (4.1) contains a restriction that the reference must bind directly to a glvalue. In the other direction, we have the identity conversion sequence from the const lvalue operand to S. Thus, /4 seems to tell us that the const lvalue operand must be converted to S, and the result should have type S.

Yet I would not expect both GCC and Clang to be wrong here, so I think that I have misunderstood the standard in this case. Surely there must be a reason why the result has type const S, but I can't figure it out.

(Even if we assume that the compiler is obligated to perform an lvalue-to-rvalue conversion on the const lvalue operand, resulting in a const prvalue, that still doesn't seem to explain the result. If this were the case, /4 would end with a const prvalue and a non-const prvalue, /5 would not apply, and we would get to /6 and the types would still not be the same. The "overload resolution" procedure prescribed there would fail since S can't be converted to any scalar types, making the program ill-formed. This interpretation thus cannot be correct either.)

--
Brian Bi
--
Std-Discussion mailing list
Std-Discussion@lists.isocpp.org
https://lists.isocpp.org/mailman/listinfo.cgi/std-discussion
--
Std-Discussion mailing list
Std-Discussion@lists.isocpp.org
https://lists.isocpp.org/mailman/listinfo.cgi/std-discussion