<div dir="ltr">It would seem that MSVC and ICC also give `const S`.<div><br></div><div><a href="https://www.godbolt.org/z/ejen4_">https://www.godbolt.org/z/ejen4_</a><br></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Tue, Sep 24, 2019 at 12:09 PM Tom Honermann &lt;<a href="mailto:tom@honermann.net">tom@honermann.net</a>&gt; wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
  
    
  
  <div bgcolor="#FFFFFF">
    <div class="gmail-m_5088853255914417597moz-cite-prefix">On 9/24/19 12:14 PM, Brian Bi via
      Std-Discussion wrote:<br>
    </div>
    <blockquote type="cite">
      
      <div dir="ltr">Thanks for the responses, everyone.
        <div><br>
        </div>
        <div>Can someone remind me which the &quot;big 4&quot; compilers are? Did
          they all consider the result type to be `const S`?</div>
      </div>
    </blockquote>
    <p>Such references generally refer to gcc, Clang, MSVC, and EDG
      derived compilers such as Intel&#39;s ICC<br>
    </p>
    <p>You can test all of them (as well as some others) at
      <a class="gmail-m_5088853255914417597moz-txt-link-freetext" href="https://www.godbolt.org" target="_blank">https://www.godbolt.org</a>.<br>
    </p>
    <p>Tom.<br>
    </p>
    <blockquote type="cite">
      <div dir="ltr">
        <div><br>
        </div>
        <div>(It seems that it is time to open a core issue and I would
          like to mention this as part of the report.)</div>
      </div>
      <br>
      <div class="gmail_quote">
        <div dir="ltr" class="gmail_attr">On Tue, Sep 24, 2019 at 4:42
          AM Krystian Stasiowski via Std-Discussion &lt;<a href="mailto:std-discussion@lists.isocpp.org" target="_blank">std-discussion@lists.isocpp.org</a>&gt;
          wrote:<br>
        </div>
        <blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
          <div dir="auto">Oops. Here it is again:
            <div dir="auto"><br>
            </div>
            <div dir="auto">I&#39;m going to break it down, word by word,
              lets see how this goes. Please, fact check this!<br>
              <br>
              <span style="font-family:monospace">#include
                &lt;type_traits&gt;</span><br style="font-family:monospace">
              <span style="font-family:monospace">struct S {};</span><br style="font-family:monospace">
              <span style="font-family:monospace">bool b;</span><br style="font-family:monospace">
              <span style="font-family:monospace">int main() <br>
                {</span><br style="font-family:monospace">
              <span style="font-family:monospace">    const S s{};</span><br style="font-family:monospace">
              <span style="font-family:monospace">   
                static_assert(std::is_same&lt;</span><span style="font-family:monospace">decltype(b ? S{} : s),
                const S&gt;::value);</span><br style="font-family:monospace">
              <span style="font-family:monospace">}</span> <br>
               
              <div>Here, <a href="http://eel.is/c++draft/expr.cond#4" target="_blank">http://eel.is/c++draft/expr.cond#4</a> applies
                because S{} and s have differing types (S and const S).
                Therefore, we must form a implicit conversion sequence
                between the two:<br>
                <br>
                Given E1 is S{}, E2 is s, T1 is S and T2 is const S:<br>
                - E2 is an lvalue, therefore the target type is const
                S&amp; <a href="http://eel.is/c++draft/expr.cond#4.1" target="_blank">http://eel.is/c++draft/expr.cond#4.1</a>.
                The target is a reference type, so we follow the rules
                of 
                <a href="http://eel.is/c++draft/over.ics.ref" target="_blank">http://eel.is/c++draft/over.ics.ref</a>  <br>
                  - S{} binds directly to the reference. The conversion
                is an identity conversion. <a href="http://eel.is/c++draft/over.ics.ref#1" target="_blank">http://eel.is/c++draft/over.ics.ref#1</a><br>
                  - <a href="http://eel.is/c++draft/expr.cond#4.1" target="_blank">http://eel.is/c++draft/expr.cond#4.1</a> mandates
                that the reference must bind directly to the glvalue. <b>This
                  is the first point of divergence</b>: If this is
                indeed referring to the operand itself, in this case
                S{}, without the temporary materialization conversion
                applied, then this sub clause does not apply, and
                instead the rules of <a href="http://eel.is/c++draft/expr.cond#4.3" target="_blank">http://eel.is/c++draft/expr.cond#4.3</a> apply,
                which state that in this case, since T2 is al least as
                cv-qualified as T1, the target type is T2, and a
                conversion sequence is formed; the result is the same
                for both. However, this would stop us dead in our
                tracks, and make the program ill-formed. <b>I don&#39;t
                  believe this is intended. </b>We will assume this is
                unintentional, and throw in the possibility that the
                conversion sequence cannot be formed, assuming that <a href="http://eel.is/c++draft/expr.cond#4.1" target="_blank">http://eel.is/c++draft/expr.cond#4.1</a> really
                does require E1 to be a glvalue without the temporary
                materialization conversion applied. </div>
              <div><br>
                Now, the calculation is complete. <b>The conversion
                  sequence for S{} to s is either not able to be formed,
                  or is an identity conversion.</b><br>
                <br>
                Now, for the second sequence:<br>
                Given E1 is s, E2 is S{}, T1 is const S and T2 is S:<br>
                - E2 is a prvalue so <a href="http://eel.is/c++draft/expr.cond#4.3" target="_blank">http://eel.is/c++draft/expr.cond#4.3</a> applies.
                T2 is less cv-qualified than T1, so 4.3.1 does not
                apply. T2 is not a base class, so 4.3.2 does not apply.
                Therefore, the target type is the type of E2 after the
                lvalue-to-rvalue, function-to-pointer, and array to
                pointer conversions are applied - these have no effect.
                The target type is S.</div>
              <div>- The target type is not a reference, so <a href="http://eel.is/c++draft/over.best.ics#6" target="_blank">http://eel.is/c++draft/over.best.ics#6</a> applies.
                The conversion sequence is the one that converts E2 to a
                prvalue of of target type, S <a href="http://eel.is/c++draft/over.best.ics#6.sentence-2" target="_blank">http://eel.is/c++draft/over.best.ics#6.sentence-2</a>.
                Top level cv-qualifiers are ignored <a href="http://eel.is/c++draft/over.best.ics#6.sentence-4" target="_blank">http://eel.is/c++draft/over.best.ics#6.sentence-4</a>.
                As they are ignored, and they both have the same class
                type, the conversion is an identity conversion <a href="http://eel.is/c++draft/over.best.ics#6.sentence-7" target="_blank">http://eel.is/c++draft/over.best.ics#6.sentence-7</a><br>
                <br>
                Side note: The standard kinda disagrees with itself
                here, it first states &quot;The implicit conversion sequence
                is the one required to convert the argument expression
                to a prvalue of the type of the parameter.&quot; (<a href="http://eel.is/c++draft/over.best.ics#6.sentence-2" target="_blank">http://eel.is/c++draft/over.best.ics#6.sentence-2</a>) and
                also says &quot;When the parameter has a class type and the
                argument expression has the same type, the implicit
                conversion sequence is an identity conversion.&quot;. We will
                assume that it means the later since it is more
                constrained, and therefore does not include the
                lvalue-to-rvalue conversion. This does not change the
                later result, as a lvalue-to-rvalue conversion would not
                discard the cv-qualifiers <a href="https://eel.is/c++draft/conv.lval#1" target="_blank">https://eel.is/c++draft/conv.lval#1</a></div>
              <div><br>
                This calculation is complete. <b>The conversion
                  sequence for s to S{} is an identity conversion.</b> <br>
                <br>
                Here are our two options now:<br>
                1. The conversion sequence for S{} to s cannot be
                formed, so the conversion sequence for s to S{} is
                applied to s <a href="http://eel.is/c++draft/expr.cond#4.sentence-7" target="_blank">http://eel.is/c++draft/expr.cond#4.sentence-7</a>.
                It has no effect on value category or type, as no
                conversion is performed.</div>
              <div>2. Both conversion sequences are formed, making the
                program ill-formed. <a href="http://eel.is/c++draft/expr.cond#4.sentence-5" target="_blank">http://eel.is/c++draft/expr.cond#4.sentence-5</a><br>
                <br>
                Since option 2 makes the program ill-formed, and the big
                4 all agree that this is a well-formed construct, we can
                assume that it does mean that in the case of 
                <a href="https://eel.is/c++draft/expr.cond#4.1" target="_blank">https://eel.is/c++draft/expr.cond#4.1</a>  E1
                must be a glvalue, the reference must bind directly to
                it, and <a href="http://eel.is/c++draft/expr.cond#4.3" target="_blank">http://eel.is/c++draft/expr.cond#4.3</a> will
                not kick in to form that implicit conversion sequence.<br>
                <br>
                From this point on, we will refer to the converted
                operands as CE1, and CE2 (even though only an identity
                conversion was done)<br>
                <br>
                Now for option 1:<br>
                - EC1 and EC2 do not have the same value category, nor
                the same type, so 
                <a href="http://eel.is/c++draft/expr.cond#5" target="_blank">http://eel.is/c++draft/expr.cond#5</a> 
                does not apply. This means the result is a prvalue. <a href="http://eel.is/c++draft/expr.cond#6" target="_blank">http://eel.is/c++draft/expr.cond#6</a><br>
                - Since the types of EC1 and EC2 differ, and both are
                class types, <a href="http://eel.is/c++draft/expr.cond#6" target="_blank">http://eel.is/c++draft/expr.cond#6</a> say
                &quot;overload resolution is used to determine the
                conversions (if any) to be applied to the operands&quot; and
                cross-references <a href="http://eel.is/c++draft/over.match.oper#3.3" target="_blank">http://eel.is/c++draft/over.match.oper#3.3</a>  and 
                <a href="http://eel.is/c++draft/over.match.oper" target="_blank">http://eel.is/c++draft/over.match.oper</a>.
                <br>
                <b><br>
                  This is the second point of divergence</b>, built in
                candidates are defined for the conditional operator, but
                since it is not a binary operator nor a unary operator,
                the entirety of 
                <a href="http://eel.is/c++draft/over.match.oper#3" target="_blank">http://eel.is/c++draft/over.match.oper#3</a> does
                not apply. The note <a href="http://eel.is/c++draft/over.match.oper#1.sentence-3" target="_blank">http://eel.is/c++draft/over.match.oper#1.sentence-3</a> does
                state that the rules in the sub-clause are used to
                determine the conversions applied to the operands, but
                there is no such clause in <a href="http://eel.is/c++draft/over.match.oper" target="_blank">http://eel.is/c++draft/over.match.oper</a> that
                describes this behavior in our case (nor even a
                semblance of one). <a href="http://eel.is/c++draft/over.match.oper#2" target="_blank">http://eel.is/c++draft/over.match.oper#2</a> says
                that overload resolution is performed to determine which
                built-in operator will be used. However, the only
                candidates defined (<a href="http://eel.is/c++draft/over.built#27" target="_blank">http://eel.is/c++draft/over.built#27</a> and <a href="http://eel.is/c++draft/over.built#28" target="_blank">http://eel.is/c++draft/over.built#28</a>)
                do not include class types, and since S cannot be
                converted to an arithmetic type, pointer type, pointer
                to member type or scoped enumeration type, none of these
                will be selected. <b>This is definitely not intended</b>,
                since it would only work for class types convertible to
                arithmetic, pointer, pointer to member and scoped
                enumeration types. <br>
                <b><br>
                  WARNING: HERE BE SPECULATION LAND<br>
                  <br>
                  This leaves us with two options, the construct is
                  ill-formed, or we can assume make an educated guess
                  based on the text.</b> Since all compilers tested
                accept this, we can assume that there is some invented
                built in candidate<font face="monospace"> T
                  operator?:(bool, T, T); </font><font face="arial,
                  sans-serif">Where T is determined as follows.<br>
                  - If both operands are of the same class type
                  (ignoring cv-qualification), the T is the cv-combined
                  type of the types of both operands</font></div>
              <div><font face="arial, sans-serif">- Otherwise, if only
                  one operand is of (possibly cv-qualified) class type
                  U, T is U<br>
                  - Otherwise, if both are class types, an attempt is
                  made to form conversion sequences between the two. If
                  both are formed, or none are, the program is
                  ill-formed. Otherwise, T is the target type of the
                  conversion sequence that was formed.<br>
                  <br>
                  (This is by no means correct, just a rough outline
                  based on my observations of the behavior of clang and
                  gcc. This <i>should</i> replicated the behavior, but
                  I have not tested all possible cases)<br>
                  <br>
                  This would result in overload resolution selecting
                  this invented operator function</font>, and the
                operands would be converted to the types of the
                parameters <a href="http://eel.is/c++draft/over.match.oper#10" target="_blank">http://eel.is/c++draft/over.match.oper#10</a>.
                When applied to our case, the converted operands are of
                type S and const S; the cv-combined type of these is
                const S, leading both operands being converted to const
                S.
                Now that the types match exactly, <a href="http://eel.is/c++draft/expr.cond#7" target="_blank">http://eel.is/c++draft/expr.cond#7</a> applies,
                and the lvalue-to-rvalue, array-to-pointer, and
                function-to-pointer standard conversions are performed
                on the second and third operands, and the result is of
                type const S, which is what clang and gcc report.</div>
              <div><b><br>
                  Here is the final tally of possible behaviors:<br>
                  1. Both conversion sequences are formed, making the
                  program ill-formed (I don&#39;t consider this an option, I
                  just included it here for the sake of being complete)</b></div>
              <div><b>2. Only the s to S{} sequence is formed, but no
                  built in candidate exists for T operator?:(T, T) where
                  T is a class type. Overload resolution fails, and the
                  program is ill formed.</b></div>
              <div><b>3. Only the s to S{} sequence is formed, and there
                  exists a candidate function T operator?:(T, T) where T
                  is const S. Overload resolution is successful, and
                  both operands are converted to const S. The expression
                  is a prvalue of type const S.</b></div>
              <div><b><br>
                </b></div>
              <div>Option 3 is the one that best illustrates the
                behavior of the major compilers, so even though it uses
                some extrapolated wording, I believe this is what is
                actually happening. After spending 2.5 hours researching
                this and tracing it though the standard, I think its
                safe to say that this wording is defective. I think that
                the fact that top level cv-qualifiers are ignored when
                forming a implicit conversion sequence was overlooked
                when this was written, or perhaps the wording was
                changed, and this was just never updated.</div>
            </div>
            <div><br>
            </div>
            <div style="font-size:100%;color:rgb(0,0,0)" dir="auto">
              <div>-------- Original message --------</div>
              <div>From: Andrew Schepler via Std-Discussion &lt;<a href="mailto:std-discussion@lists.isocpp.org" target="_blank">std-discussion@lists.isocpp.org</a>&gt;
              </div>
              <div>Date: 9/24/19 05:21 (GMT-05:00) </div>
              <div>To: <a href="mailto:std-discussion@lists.isocpp.org" target="_blank">std-discussion@lists.isocpp.org</a>
              </div>
              <div>Cc: Andrew Schepler &lt;<a href="mailto:aschepler@gmail.com" target="_blank">aschepler@gmail.com</a>&gt; </div>
              <div>Subject: Re: [std-discussion] Conditional operator
                with const lvalue and non-const prvalue </div>
              <div><br>
              </div>
            </div>
            <div dir="ltr">
              <div dir="ltr">It looks like you accidentally sent that
                last message to just me, and not the mailing list:</div>
              <br>
              <div class="gmail_quote">
                <div dir="ltr" class="gmail_attr">On Tue, Sep 24, 2019
                  at 12:29 AM Krystian Stasiowski &lt;<a href="mailto:sdkrystian@gmail.com" target="_blank">sdkrystian@gmail.com</a>&gt;
                  wrote:<br>
                </div>
                <blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
                  <div dir="ltr">
                    <div dir="ltr">I&#39;m going to break it down, word by
                      word, lets see how this goes. Please, fact check
                      this!<br>
                      <br>
                      <span style="font-family:monospace">#include
                        &lt;type_traits&gt;</span><br style="font-family:monospace">
                      <span style="font-family:monospace">struct S {};</span><br style="font-family:monospace">
                      <span style="font-family:monospace">bool b;</span><br style="font-family:monospace">
                      <span style="font-family:monospace">int main() <br>
                        {</span><br style="font-family:monospace">
                      <span style="font-family:monospace">    const S
                        s{};</span><br style="font-family:monospace">
                      <span style="font-family:monospace">   
                        static_assert(std::is_same&lt;</span><span style="font-family:monospace">decltype(b ? S{} :
                        s), const S&gt;::value);</span><br style="font-family:monospace">
                      <span style="font-family:monospace">}</span> <br>
                       
                      <div>Here, <a href="http://eel.is/c++draft/expr.cond#4" target="_blank">http://eel.is/c++draft/expr.cond#4</a> applies
                        because S{} and s have differing types (S and
                        const S). Therefore, we must form a implicit
                        conversion sequence between the two:<br>
                        <br>
                        Given E1 is S{}, E2 is s, T1 is S and T2 is
                        const S:<br>
                        - E2 is an lvalue, therefore the target type is
                        const S&amp; <a href="http://eel.is/c++draft/expr.cond#4.1" target="_blank">http://eel.is/c++draft/expr.cond#4.1</a>.
                        The target is a reference type, so we follow the
                        rules of 
                        <a href="http://eel.is/c++draft/over.ics.ref" target="_blank">http://eel.is/c++draft/over.ics.ref</a>  <br>
                          - S{} binds directly to the reference. The
                        conversion is an identity conversion. <a href="http://eel.is/c++draft/over.ics.ref#1" target="_blank">http://eel.is/c++draft/over.ics.ref#1</a><br>
                          - <a href="http://eel.is/c++draft/expr.cond#4.1" target="_blank">http://eel.is/c++draft/expr.cond#4.1</a> mandates
                        that the reference must bind directly to the
                        glvalue. <b>This is the first point of
                          divergence</b>: If this is indeed referring to
                        the operand itself, in this case S{}, without
                        the temporary materialization conversion
                        applied, then this sub clause does not apply,
                        and instead the rules of <a href="http://eel.is/c++draft/expr.cond#4.3" target="_blank">http://eel.is/c++draft/expr.cond#4.3</a> apply,
                        which state that in this case, since T2 is al
                        least as cv-qualified as T1, the target type is
                        T2, and a conversion sequence is formed; the
                        result is the same for both. However, this would
                        stop us dead in our tracks, and make the program
                        ill-formed. <b>I don&#39;t believe this is
                          intended. </b>We will assume this is
                        unintentional, and throw in the possibility that
                        the conversion sequence cannot be formed,
                        assuming that <a href="http://eel.is/c++draft/expr.cond#4.1" target="_blank">http://eel.is/c++draft/expr.cond#4.1</a> really
                        does require E1 to be a glvalue without the
                        temporary materialization conversion applied. </div>
                      <div><br>
                        Now, the calculation is complete. <b>The
                          conversion sequence for S{} to s is either not
                          able to be formed, or is an identity
                          conversion.</b><br>
                        <br>
                        Now, for the second sequence:<br>
                        Given E1 is s, E2 is S{}, T1 is const S and T2
                        is S:<br>
                        - E2 is a prvalue so <a href="http://eel.is/c++draft/expr.cond#4.3" target="_blank">http://eel.is/c++draft/expr.cond#4.3</a> applies.
                        T2 is less cv-qualified than T1, so 4.3.1 does
                        not apply. T2 is not a base class, so 4.3.2 does
                        not apply. Therefore, the target type is the
                        type of E2 after the lvalue-to-rvalue,
                        function-to-pointer, and array to pointer
                        conversions are applied - these have no effect.
                        The target type is S.</div>
                      <div>- The target type is not a reference, so <a href="http://eel.is/c++draft/over.best.ics#6" target="_blank">http://eel.is/c++draft/over.best.ics#6</a> applies.
                        The conversion sequence is the one that converts
                        E2 to a prvalue of of target type, S <a href="http://eel.is/c++draft/over.best.ics#6.sentence-2" target="_blank">http://eel.is/c++draft/over.best.ics#6.sentence-2</a>.
                        Top level cv-qualifiers are ignored <a href="http://eel.is/c++draft/over.best.ics#6.sentence-4" target="_blank">http://eel.is/c++draft/over.best.ics#6.sentence-4</a>.
                        As they are ignored, and they both have the same
                        class type, the conversion is an identity
                        conversion <a href="http://eel.is/c++draft/over.best.ics#6.sentence-7" target="_blank">http://eel.is/c++draft/over.best.ics#6.sentence-7</a><br>
                        <br>
                        Side note: The standard kinda disagrees with
                        itself here, it first states &quot;The implicit
                        conversion sequence is the one required to
                        convert the argument expression to a prvalue of
                        the type of the parameter.&quot; (<a href="http://eel.is/c++draft/over.best.ics#6.sentence-2" target="_blank">http://eel.is/c++draft/over.best.ics#6.sentence-2</a>) and
                        also says &quot;When the parameter has a class type
                        and the argument expression has the same type,
                        the implicit conversion sequence is an identity
                        conversion.&quot;. We will assume that it means the
                        later since it is more constrained, and
                        therefore does not include the lvalue-to-rvalue
                        conversion. This does not change the later
                        result, as a lvalue-to-rvalue conversion would
                        not discard the cv-qualifiers <a href="https://eel.is/c++draft/conv.lval#1" target="_blank">https://eel.is/c++draft/conv.lval#1</a></div>
                      <div><br>
                        This calculation is complete. <b>The conversion
                          sequence for s to S{} is an identity
                          conversion.</b> <br>
                        <br>
                        Here are our two options now:<br>
                        1. The conversion sequence for S{} to s cannot
                        be formed, so the conversion sequence for s to
                        S{} is applied to s <a href="http://eel.is/c++draft/expr.cond#4.sentence-7" target="_blank">http://eel.is/c++draft/expr.cond#4.sentence-7</a>.
                        It has no effect on value category or type, as
                        no conversion is performed.</div>
                      <div>2. Both conversion sequences are formed,
                        making the program ill-formed. <a href="http://eel.is/c++draft/expr.cond#4.sentence-5" target="_blank">http://eel.is/c++draft/expr.cond#4.sentence-5</a><br>
                        <br>
                        Since option 2 makes the program ill-formed, and
                        the big 4 all agree that this is a well-formed
                        construct, we can assume that it does mean that
                        in the case of 
                        <a href="https://eel.is/c++draft/expr.cond#4.1" target="_blank">https://eel.is/c++draft/expr.cond#4.1</a>  E1
                        must be a glvalue, the reference must bind
                        directly to it, and <a href="http://eel.is/c++draft/expr.cond#4.3" target="_blank">http://eel.is/c++draft/expr.cond#4.3</a> will
                        not kick in to form that implicit conversion
                        sequence.<br>
                        <br>
                        From this point on, we will refer to the
                        converted operands as CE1, and CE2 (even though
                        only an identity conversion was done)<br>
                        <br>
                        Now for option 1:<br>
                        - EC1 and EC2 do not have the same value
                        category, nor the same type, so 
                        <a href="http://eel.is/c++draft/expr.cond#5" target="_blank">http://eel.is/c++draft/expr.cond#5</a> 
                        does not apply. This means the result is a
                        prvalue. <a href="http://eel.is/c++draft/expr.cond#6" target="_blank">http://eel.is/c++draft/expr.cond#6</a><br>
                        - Since the types of EC1 and EC2 differ, and
                        both are class types, <a href="http://eel.is/c++draft/expr.cond#6" target="_blank">http://eel.is/c++draft/expr.cond#6</a> say
                        &quot;overload resolution is used to determine the
                        conversions (if any) to be applied to the
                        operands&quot; and cross-references <a href="http://eel.is/c++draft/over.match.oper#3.3" target="_blank">http://eel.is/c++draft/over.match.oper#3.3</a>  and 
                        <a href="http://eel.is/c++draft/over.match.oper" target="_blank">http://eel.is/c++draft/over.match.oper</a>.
                        <br>
                        <b><br>
                          This is the second point of divergence</b>,
                        built in candidates are defined for the
                        conditional operator, but since it is not a
                        binary operator nor a unary operator, the
                        entirety of 
                        <a href="http://eel.is/c++draft/over.match.oper#3" target="_blank">http://eel.is/c++draft/over.match.oper#3</a> does
                        not apply. The note <a href="http://eel.is/c++draft/over.match.oper#1.sentence-3" target="_blank">http://eel.is/c++draft/over.match.oper#1.sentence-3</a> does
                        state that the rules in the sub-clause are used
                        to determine the conversions applied to the
                        operands, but there is no such clause in <a href="http://eel.is/c++draft/over.match.oper" target="_blank">http://eel.is/c++draft/over.match.oper</a> that
                        describes this behavior in our case (nor even a
                        semblance of one). <a href="http://eel.is/c++draft/over.match.oper#2" target="_blank">http://eel.is/c++draft/over.match.oper#2</a> says
                        that overload resolution is performed to
                        determine which built-in operator will be used.
                        However, the only candidates defined (<a href="http://eel.is/c++draft/over.built#27" target="_blank">http://eel.is/c++draft/over.built#27</a> and <a href="http://eel.is/c++draft/over.built#28" target="_blank">http://eel.is/c++draft/over.built#28</a>)
                        do not include class types, and since S cannot
                        be converted to an arithmetic type, pointer
                        type, pointer to member type or scoped
                        enumeration type, none of these will be
                        selected. <b>This is definitely not intended</b>,
                        since it would only work for class types
                        convertible to arithmetic, pointer, pointer to
                        member and scoped enumeration types. <br>
                        <b><br>
                          WARNING: HERE BE SPECULATION LAND<br>
                          <br>
                          This leaves us with two options, the construct
                          is ill-formed, or we can assume make an
                          educated guess based on the text.</b> Since
                        all compilers tested accept this, we can assume
                        that there is some invented built in candidate<font face="monospace"> T operator?:(bool, T, T); </font><font face="arial, sans-serif">Where T is determined
                          as follows.<br>
                          - If both operands are of the same class type
                          (ignoring cv-qualification), the T is the
                          cv-combined type of the types of both operands</font></div>
                      <div><font face="arial, sans-serif">- Otherwise,
                          if only one operand is of (possibly
                          cv-qualified) class type U, T is U<br>
                          - Otherwise, if both are class types, an
                          attempt is made to form conversion sequences
                          between the two. If both are formed, or none
                          are, the program is ill-formed. Otherwise, T
                          is the target type of the conversion sequence
                          that was formed.<br>
                          <br>
                          (This is by no means correct, just a rough
                          outline based on my observations of the
                          behavior of clang and gcc. This <i>should</i> replicated
                          the behavior, but I have not tested all
                          possible cases)<br>
                          <br>
                          This would result in overload resolution
                          selecting this invented operator function</font>,
                        and the operands would be converted to the types
                        of the parameters <a href="http://eel.is/c++draft/over.match.oper#10" target="_blank">http://eel.is/c++draft/over.match.oper#10</a>.
                        When applied to our case, the converted operands
                        are of type S and const S; the cv-combined type
                        of these is const S, leading both operands being
                        converted to const S.
                        Now that the types match exactly, <a href="http://eel.is/c++draft/expr.cond#7" target="_blank">http://eel.is/c++draft/expr.cond#7</a> applies,
                        and the lvalue-to-rvalue, array-to-pointer, and
                        function-to-pointer standard conversions are
                        performed on the second and third operands, and
                        the result is of type const S, which is what
                        clang and gcc report.</div>
                      <div><b><br>
                          Here is the final tally of possible behaviors:<br>
                          1. Both conversion sequences are formed,
                          making the program ill-formed (I don&#39;t
                          consider this an option, I just included it
                          here for the sake of being complete)</b></div>
                      <div><b>2. Only the s to S{} sequence is formed,
                          but no built in candidate exists for T
                          operator?:(T, T) where T is a class type.
                          Overload resolution fails, and the program is
                          ill formed.</b></div>
                      <div><b>3. Only the s to S{} sequence is formed,
                          and there exists a candidate function T
                          operator?:(T, T) where T is const S. Overload
                          resolution is successful, and both operands
                          are converted to const S. The expression is a
                          prvalue of type const S.</b></div>
                      <div><b><br>
                        </b></div>
                      <div>Option 3 is the one that best illustrates the
                        behavior of the major compilers, so even though
                        it uses some extrapolated wording, I believe
                        this is what is actually happening. After
                        spending 2.5 hours researching this and tracing
                        it though the standard, I think its safe to say
                        that this wording is defective. I think that the
                        fact that top level cv-qualifiers are ignored
                        when forming a implicit conversion sequence was
                        overlooked when this was written, or perhaps the
                        wording was changed, and this was just never
                        updated.</div>
                    </div>
                    <br>
                    <div class="gmail_quote">
                      <div dir="ltr" class="gmail_attr">On Mon, Sep 23,
                        2019 at 7:42 PM Andrew Schepler &lt;<a href="mailto:aschepler@gmail.com" target="_blank">aschepler@gmail.com</a>&gt;
                        wrote:<br>
                      </div>
                      <blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
                        <div dir="ltr">
                          <div>Yes, the conversion from prvalue S{} to
                            type const S&amp; would bind directly, but
                            it &quot;is said to bind directly to the
                            initializer expression&quot;, and the initializer
                            expression &quot;S{}&quot; is not a glvalue. A bit
                            confusing, since the reference &quot;binds to&quot;
                            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.</div>
                          <div><br>
                          </div>
                          There certainly is an implicit conversion
                          sequence from an lvalue of type const S to
                          type S, via the copy constructor. Otherwise we
                          couldn&#39;t do:
                          <div><br>
                          </div>
                          <div>struct S {};</div>
                          <div>void f(S) {}</div>
                          <div>int main() {</div>
                          <div>    const S s;</div>
                          <div>    f(s);</div>
                          <div>}</div>
                          <div><br>
                          </div>
                          <div>But in converting &quot;S{}&quot; to a type related
                            to &quot;s&quot;, wouldn&#39;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:</div>
                          <div><br>
                          </div>
                          <div>C++17 N4659:</div>
                          <div>(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,</div>
                          <div>(4.3.2) otherwise, ...</div>
                          <div><br>
                          </div>
                          <div>C++20 latest at <a href="https://timsong-cpp.github.io/cppwp/expr.cond" target="_blank">https://timsong-cpp.github.io/cppwp/expr.cond</a> :</div>
                          <div>(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,</div>
                          <div>(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,</div>
                          <div>(4.3.3) otherwise, ...</div>
                          <div><br>
                          </div>
                          <div>But I don&#39;t see how that change would
                            make any implicit conversion sequence
                            unavailable. Which would mean both are
                            possible and &quot;b ? S{} : s&quot; 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&#39;t say that.)</div>
                          <div><br>
                          </div>
                          <div><br>
                          </div>
                          <div><br>
                          </div>
                        </div>
                        <br>
                        <div class="gmail_quote">
                          <div dir="ltr" class="gmail_attr">On Mon, Sep
                            23, 2019 at 6:51 PM Krystian Stasiowski via
                            Std-Discussion &lt;<a href="mailto:std-discussion@lists.isocpp.org" target="_blank">std-discussion@lists.isocpp.org</a>&gt;
                            wrote:<br>
                          </div>
                          <blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
                            <div dir="ltr">Only the last case where the
                              initializer expression is converted does
                              it not bind directly 
                              <a href="http://eel.is/c++draft/dcl.init.ref#5.4" target="_blank">http://eel.is/c++draft/dcl.init.ref#5.4</a><br>
                              <br>
                              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. <br>
                              <br>
                              I think.</div>
                            <br>
                            <div class="gmail_quote">
                              <div dir="ltr" class="gmail_attr">On Mon,
                                Sep 23, 2019 at 11:02 AM Brian Bi via
                                Std-Discussion &lt;<a href="mailto:std-discussion@lists.isocpp.org" target="_blank">std-discussion@lists.isocpp.org</a>&gt;
                                wrote:<br>
                              </div>
                              <blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
                                <div dir="ltr">
                                  <div>Consider the following:</div>
                                  <div><br>
                                  </div>
                                  <font face="monospace">#include
                                    &lt;type_traits&gt;<br>
                                    struct S {};<br>
                                    bool b;<br>
                                    int main() {<br>
                                        const S s {};<br>
                                       
                                    static_assert(std::is_same&lt;decltype(b
                                    ? S{} : s), const S&gt;::value);<br>
                                    }</font><br clear="all">
                                  <div><br>
                                  </div>
                                  <div><a href="http://coliru.stacked-crooked.com/a/49d1853146cbcdc1" target="_blank">Here</a>
                                    GCC and Clang confirm that the type
                                    is correct. But I can&#39;t figure out
                                    why. My reading of the standard is
                                    that the result should be a prvalue
                                    of type S, not const S.</div>
                                  <div><br>
                                  </div>
                                  <div>[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&amp;
                                    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.</div>
                                  <div><br>
                                  </div>
                                  <div>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&#39;t figure it
                                    out.</div>
                                  <div><br>
                                  </div>
                                  <div>(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&#39;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 &quot;overload
                                    resolution&quot; procedure prescribed
                                    there would fail since S can&#39;t be
                                    converted to any scalar types,
                                    making the program ill-formed. This
                                    interpretation thus cannot be
                                    correct either.)</div>
                                  <div><br>
                                  </div>
                                  -- <br>
                                  <div dir="ltr" class="gmail-m_5088853255914417597gmail-m_-1150035348314261911gmail-m_-4269439385936587630gmail-m_3954054080281030732gmail-m_-8754518339240940838gmail-m_-3967511304589770169gmail_signature">
                                    <div dir="ltr">
                                      <div>
                                        <div dir="ltr"><font color="#c0c0c0"><i>Brian Bi</i></font><br>
                                        </div>
                                      </div>
                                    </div>
                                  </div>
                                </div>
                                -- <br>
                                Std-Discussion mailing list<br>
                                <a href="mailto:Std-Discussion@lists.isocpp.org" target="_blank">Std-Discussion@lists.isocpp.org</a><br>
                                <a href="https://lists.isocpp.org/mailman/listinfo.cgi/std-discussion" rel="noreferrer" target="_blank">https://lists.isocpp.org/mailman/listinfo.cgi/std-discussion</a><br>
                              </blockquote>
                            </div>
                            -- <br>
                            Std-Discussion mailing list<br>
                            <a href="mailto:Std-Discussion@lists.isocpp.org" target="_blank">Std-Discussion@lists.isocpp.org</a><br>
                            <a href="https://lists.isocpp.org/mailman/listinfo.cgi/std-discussion" rel="noreferrer" target="_blank">https://lists.isocpp.org/mailman/listinfo.cgi/std-discussion</a></blockquote>
                        </div>
                      </blockquote>
                    </div>
                  </div>
                </blockquote>
                <div><br>
                </div>
                <div>&quot;Identity conversion&quot; does not mean there is no
                  change in type or value category. It only means that
                  the conversion sequence is associated with category
                  &quot;Identity conversion&quot; for purposes of comparing which
                  implicit conversion sequence is better during overload
                  resolution. In [over.best.ics]/6, &quot;Any difference in
                  top-level cv-qualification is subsumed by the
                  initialization itself and does not constitute a
                  conversion.&quot; doesn&#39;t mean that the cv-qualifiers of
                  the target type must match the expression type; it
                  means that if the only difference in qualifiers is at
                  the top level, the implicit conversion sequence used
                  in comparisons doesn&#39;t include a qualification
                  conversion. In the same paragraph, &quot;When the parameter
                  has a class type and the argument expression has the
                  same type, the implicit conversion sequence is an
                  identity conversion.&quot; also doesn&#39;t change the target
                  type; it means that even if the actual initialization
                  requires a move/copy constructor, the ICS category is
                  Identity and the rank is Exact Match.</div>
                <div><br>
                </div>
                <div>So the conversion of &quot;s&quot; to a type related to &quot;S{}&quot;
                  can be from &quot;const S&quot; to &quot;S&quot;. If we suppose that the
                  conversion from &quot;S{}&quot; to a type related to &quot;s&quot; cannot
                  be formed, I agree the final type should be &quot;S&quot; and
                  not &quot;const S&quot;. Though I still don&#39;t see why (4.3)
                  wouldn&#39;t apply to the conversion of &quot;S{}&quot; if (4.1)
                  doesn&#39;t. Possibly the Standard wording there could be
                  fixed or clarified.</div>
                <div><br>
                </div>
                <div>I also agree there seems to be some wording missing
                  about exactly when and how overload resolution is
                  involved in semantic analysis of the conditional
                  expression. Something should say that it goes through
                  the same process as unary and binary expressions in
                  [over.match.oper] using the phony name &quot;operator?:&quot;,
                  but the only candidate functions are the built-in
                  candidate set. But I think it&#39;s intentional that
                  [over.built] does not include any signatures involving
                  class types, because getting past [expr.cond]/5 to the
                  &quot;Otherwise&quot; [expr.cond]/6 where overload resolution
                  gets involved is meant to be just for cases where the
                  types aren&#39;t similar enough to use &quot;Exact Match&quot; or
                  &quot;Derived To Base&quot; conversion sequences, and we can&#39;t
                  convert either expression to the other&#39;s type, so the
                  final option is to use one or two conversion functions
                  to convert each class type operand to a non-class
                  type.</div>
                <div><br>
                </div>
                <div>Though this is backwards from the normal pattern:
                  for most compound expressions, we can start with
                  [over.match.oper]. At the very start, it says
                  overloading does not apply if no operand has class or
                  enum type, so only [expr] applies. Otherwise, overload
                  resolution determines whether the compound expression
                  is a function call or has the &quot;built-in&quot; meaning in
                  [expr], and if it has the built-in meaning, the
                  selected built-in candidate function determines any
                  conversions to be applied before the semantics of
                  [expr]. For a ternary conditional expression, it only
                  makes sense to start analysis at [expr.cond]. If we
                  pass the &quot;Otherwise&quot; at the start of [expr.cond], then
                  [over.match.oper] applies, forcing the result type to
                  be a non-class type. If that&#39;s really how it should be
                  read, I think it would be good to have something
                  mention that exception.</div>
                <div><br>
                </div>
                <div>(Some of your phrasing seems to imply &quot;since
                  compilers do this, the Standard must be interpreted in
                  a way consistent with that result&quot;, and I&#39;d be careful
                  about that reversal. Compiler teams are capable of
                  error, and there might be some common reason multiple
                  implementations use logic that matches the Standard
                  most of the time but happens to give a consistent
                  different result in one corner case not specifically
                  intended. There&#39;s also the possibility the Standard
                  has a defect or insufficiently clear wording. And of
                  course common sense can enter into it: if selecting
                  one of two expressions with a type differing only by
                  &quot;const&quot; can ever make the code ill-formed, something
                  is wrong.)</div>
                <div><br>
                </div>
              </div>
            </div>
          </div>
          -- <br>
          Std-Discussion mailing list<br>
          <a href="mailto:Std-Discussion@lists.isocpp.org" target="_blank">Std-Discussion@lists.isocpp.org</a><br>
          <a href="https://lists.isocpp.org/mailman/listinfo.cgi/std-discussion" rel="noreferrer" target="_blank">https://lists.isocpp.org/mailman/listinfo.cgi/std-discussion</a><br>
        </blockquote>
      </div>
      <br clear="all">
      <div><br>
      </div>
      -- <br>
      <div dir="ltr" class="gmail-m_5088853255914417597gmail_signature">
        <div dir="ltr">
          <div>
            <div dir="ltr"><font color="#c0c0c0"><i>Brian Bi</i></font><br>
            </div>
          </div>
        </div>
      </div>
      <br>
      <fieldset class="gmail-m_5088853255914417597mimeAttachmentHeader"></fieldset>
    </blockquote>
    <p><br>
    </p>
  </div>

</blockquote></div><br clear="all"><div><br></div>-- <br><div dir="ltr" class="gmail_signature"><div dir="ltr"><div><div dir="ltr"><font color="#c0c0c0"><i>Brian Bi</i></font><br><div></div><div></div><div></div></div></div></div></div>

