Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c++ > #7527 > unrolled thread

Slicing with ?: operator - expected?

Started byVictor Bazarov <v.bazarov@comcast.invalid>
First post2011-07-06 09:50 -0400
Last post2011-07-07 01:47 +0100
Articles 11 — 6 participants

Back to article view | Back to comp.lang.c++


Contents

  Slicing with ?: operator - expected? Victor Bazarov <v.bazarov@comcast.invalid> - 2011-07-06 09:50 -0400
    Re: Slicing with ?: operator - expected? Michael DOUBEZ <michael.doubez@free.fr> - 2011-07-06 07:41 -0700
      Re: Slicing with ?: operator - expected? Michael DOUBEZ <michael.doubez@free.fr> - 2011-07-06 07:50 -0700
    Re: Slicing with ?: operator - expected? Bart van Ingen Schenau <bart@ingen.ddns.info> - 2011-07-06 16:41 +0200
      Re: Slicing with ?: operator - expected? Victor Bazarov <v.bazarov@comcast.invalid> - 2011-07-06 11:37 -0400
        Re: Slicing with ?: operator - expected? "Balog Pal" <pasa@lib.hu> - 2011-07-06 23:17 +0200
    Re: Slicing with ?: operator - expected? Joe Greer <jgreer@doubletake.com> - 2011-07-06 21:14 +0000
      Re: Slicing with ?: operator - expected? Victor Bazarov <v.bazarov@comcast.invalid> - 2011-07-08 15:56 -0400
        Re: Slicing with ?: operator - expected? Joe Greer <jgreer@doubletake.com> - 2011-07-11 16:35 +0000
          Re: Slicing with ?: operator - expected? Victor Bazarov <v.bazarov@comcast.invalid> - 2011-07-11 13:05 -0400
    Re: Slicing with ?: operator - expected? "Paul" <pchristor@yahoo.co.uk> - 2011-07-07 01:47 +0100

#7527 — Slicing with ?: operator - expected?

FromVictor Bazarov <v.bazarov@comcast.invalid>
Date2011-07-06 09:50 -0400
SubjectSlicing with ?: operator - expected?
Message-ID<iv1p6s$acb$1@dont-email.me>
Hello All,

Here is the code:

    class Foo {
    public:
       virtual void bar() const = 0;
    };

    #include <ostream>
    #include <iostream>

    class Foo1 : public Foo {
       void bar() const { std::cout << "Foo1::bar\n"; }
    };

    class Foo2 : public Foo {
       void bar() const { std::cout << "Foo2::bar\n"; }
    };

    void foo(const Foo* pFoo)
    {
       (pFoo ? *pFoo : Foo1()).bar(); // line 19 ************
    }

    void blah(const Foo& rFoo)
    {
       foo(&rFoo);
    }

    int main()
    {
       blah(Foo2());
    }

Line 19 is the line in question.  Could you please interpret it for me? 
  It seems to skip the virtual function dispatch, and attempt to call 
the pure function.  The common type of *pFoo and Foo1() is 'class Foo', 
and the compiler seems to resolve the 'bar' statically, without the use 
of virtual function mechanism, as if there is an instance of class Foo 
/sliced/ from both original objects.  Is that supposed to happen? 
Standard chapter and verse would be helpful.

To avoid a pure function call I can implement the base class' member 
function 'bar'.  The problem is that in that case I get the dynamic 
dispatch I need (I expected Foo2.bar() to be called), I am getting the 
base class.  Is that what *should* happen?

Thanks!

V
-- 
I do not respond to top-posted replies, please don't ask

[toc] | [next] | [standalone]


#7528

FromMichael DOUBEZ <michael.doubez@free.fr>
Date2011-07-06 07:41 -0700
Message-ID<ef2eb2ce-8804-4f73-a6ac-61fc189c22e1@x16g2000yqg.googlegroups.com>
In reply to#7527
On Jul 6, 3:50 pm, Victor Bazarov <v.baza...@comcast.invalid> wrote:
> Hello All,
>
> Here is the code:
>
>     class Foo {
>     public:
>        virtual void bar() const = 0;
>     };
>
>     #include <ostream>
>     #include <iostream>
>
>     class Foo1 : public Foo {
>        void bar() const { std::cout << "Foo1::bar\n"; }
>     };
>
>     class Foo2 : public Foo {
>        void bar() const { std::cout << "Foo2::bar\n"; }
>     };
>
>     void foo(const Foo* pFoo)
>     {
>        (pFoo ? *pFoo : Foo1()).bar(); // line 19 ************
>     }
>
>     void blah(const Foo& rFoo)
>     {
>        foo(&rFoo);
>     }
>
>     int main()
>     {
>        blah(Foo2());
>     }
>
> Line 19 is the line in question.  Could you please interpret it for me?
>   It seems to skip the virtual function dispatch, and attempt to call
> the pure function.  The common type of *pFoo and Foo1() is 'class Foo',
> and the compiler seems to resolve the 'bar' statically, without the use
> of virtual function mechanism, as if there is an instance of class Foo
> /sliced/ from both original objects.  Is that supposed to happen?
> Standard chapter and verse would be helpful.

My gcc (4.3.3) doesn't even compile it:

fooTest.cc: In function 'void foo(const Foo*)':
fooTest.cc:19: erreur: cannot allocate an object of abstract type
'const Foo'
fooTest.cc:1: note:   because the following virtual functions are pure
within 'const Foo':
fooTest.cc:3: note:     virtual void Foo::bar() const
fooTest.cc:19: erreur: cannot allocate an object of abstract type
'Foo'
fooTest.cc:1: note:   since type 'Foo' has pure virtual functions

It seems the ?: tries to make a copy but the following is accepted:

void foo(const Foo* pFoo)
{
  const Foo1 foo1;
  (pFoo ? *pFoo : foo1).bar();
}

> To avoid a pure function call I can implement the base class' member
> function 'bar'.  The problem is that in that case I get the dynamic
> dispatch I need (I expected Foo2.bar() to be called), I am getting the
> base class.  Is that what *should* happen?

The ?: seems to return a const Foo and not a const Foo& as might be
expected.
gcc 4.3.3 seems to be right because none of the rules of §5.16/3
matches your case.

Note: in n3242 a line reads "If E2 is an xvalue |...]" *xvalue* must
be a typo. Does anyone know if it has been fixed in the new standard ?

--
Michael

[toc] | [prev] | [next] | [standalone]


#7529

FromMichael DOUBEZ <michael.doubez@free.fr>
Date2011-07-06 07:50 -0700
Message-ID<91910824-caf9-4430-8f98-ec97ed3a76f4@k27g2000yqn.googlegroups.com>
In reply to#7528
On Jul 6, 4:41 pm, Michael DOUBEZ <michael.dou...@free.fr> wrote:
[...]
> Note: in n3242 a line reads "If E2 is an xvalue |...]" *xvalue* must
> be a typo. Does anyone know if it has been fixed in the new standard ?

Oups. Just discovering the xvalue expression.

--
Michael

[toc] | [prev] | [next] | [standalone]


#7531

FromBart van Ingen Schenau <bart@ingen.ddns.info>
Date2011-07-06 16:41 +0200
Message-ID<iv1s6f$pde$1@localhost.localdomain>
In reply to#7527
Victor Bazarov Wrote:

> Hello All,
> 
> Here is the code:
> 
>     class Foo {
>     public:
>        virtual void bar() const = 0;
>     };
> 
>     #include <ostream>
>     #include <iostream>
> 
>     class Foo1 : public Foo {
>        void bar() const { std::cout << "Foo1::bar\n"; }
>     };
> 
>     class Foo2 : public Foo {
>        void bar() const { std::cout << "Foo2::bar\n"; }
>     };
> 
>     void foo(const Foo* pFoo)
>     {
>        (pFoo ? *pFoo : Foo1()).bar(); // line 19 ************
>     }
> 
>     void blah(const Foo& rFoo)
>     {
>         foo(&rFoo);
>     }
> 
>     int main()
>     {
>        blah(Foo2());
>     }
> 
> Line 19 is the line in question.  Could you please interpret it for me? 
>   It seems to skip the virtual function dispatch, and attempt to call 
> the pure function.  The common type of *pFoo and Foo1() is 'class
> Foo', and the compiler seems to resolve the 'bar' statically, without 
> the use of virtual function mechanism, as if there is an instance of 
> class Foo /sliced/ from both original objects.  Is that supposed to 
> happen?

Yes, that is supposed to happen.
The conditional operator (?:) is defined to only yield rvalues, and never a
reference.
This means that objects of class-type indeed get sliced by the ?: operator.

> 
> Standard chapter and verse would be helpful.

The behaviour of the conditional operator is described in section 5.16
[expr.cond], where especially the last paragraph is of relevance here.

> 
> Thanks!
> 
> V

Bart v Ingen Schenau

[toc] | [prev] | [next] | [standalone]


#7534

FromVictor Bazarov <v.bazarov@comcast.invalid>
Date2011-07-06 11:37 -0400
Message-ID<iv1vf1$koa$1@dont-email.me>
In reply to#7531
On 7/6/2011 10:41 AM, Bart van Ingen Schenau wrote:
> Victor Bazarov Wrote:
>
>> Hello All,
>>
>> Here is the code:
>>
>>      class Foo {
>>      public:
>>         virtual void bar() const = 0;
>>      };
>>
>>      #include<ostream>
>>      #include<iostream>
>>
>>      class Foo1 : public Foo {
>>         void bar() const { std::cout<<  "Foo1::bar\n"; }
>>      };
>>
>>      class Foo2 : public Foo {
>>         void bar() const { std::cout<<  "Foo2::bar\n"; }
>>      };
>>
>>      void foo(const Foo* pFoo)
>>      {
>>         (pFoo ? *pFoo : Foo1()).bar(); // line 19 ************
>>      }
>>
>>      void blah(const Foo&  rFoo)
>>      {
>>          foo(&rFoo);
>>      }
>>
>>      int main()
>>      {
>>         blah(Foo2());
>>      }
>>
>> Line 19 is the line in question.  Could you please interpret it for me?
>>    It seems to skip the virtual function dispatch, and attempt to call
>> the pure function.  The common type of *pFoo and Foo1() is 'class
>> Foo', and the compiler seems to resolve the 'bar' statically, without
>> the use of virtual function mechanism, as if there is an instance of
>> class Foo /sliced/ from both original objects.  Is that supposed to
>> happen?
>
> Yes, that is supposed to happen.
> The conditional operator (?:) is defined to only yield rvalues, and never a
> reference.

I don't think it's correct.  If both expressions after the '?' are 
lvalues of the same type, the resulting expression should be an lvalue. 
  Consider:

    int x, y; // zero-initialized
    int& getx() { return x; }
    int& gety() { return y; }

    void increment(int& i) { ++i; }

    int main(int argc, char **argv)
    {
       increment(argc % 2 ? getx() : gety());
    }

Or do you think it shouldn't compile?

> This means that objects of class-type indeed get sliced by the ?: operator.
>
>>
>> Standard chapter and verse would be helpful.
>
> The behaviour of the conditional operator is described in section 5.16
> [expr.cond], where especially the last paragraph is of relevance here.
>
>>
>> Thanks!
>>
>> V
>
> Bart v Ingen Schenau
>

V
-- 
I do not respond to top-posted replies, please don't ask

[toc] | [prev] | [next] | [standalone]


#7548

From"Balog Pal" <pasa@lib.hu>
Date2011-07-06 23:17 +0200
Message-ID<iv2je4$lo3$1@news.ett.com.ua>
In reply to#7534
"Victor Bazarov" <v.bazarov@comcast.invalid>
>>>         (pFoo ? *pFoo : Foo1()).bar(); // line 19 ************
>>>
>>> Line 19 is the line in question.  Could you please interpret it for me?
>>>    It seems to skip the virtual function dispatch, and attempt to call
>>> the pure function.  The common type of *pFoo and Foo1() is 'class
>>> Foo', and the compiler seems to resolve the 'bar' statically, without
>>> the use of virtual function mechanism, as if there is an instance of
>>> class Foo /sliced/ from both original objects.  Is that supposed to
>>> happen?
>>
>> Yes, that is supposed to happen.

Yes.

>> The conditional operator (?:) is defined to only yield rvalues, and never 
>> a
>> reference.

That is inaccurate for general. But in this case Foo1() is rvalue, so ?: 
will provide rvalue.

>> The behaviour of the conditional operator is described in section 5.16
>> [expr.cond], where especially the last paragraph is of relevance here.

That says:

"If the conversion is applied, E1 is changed to an rvalue of type T2 that 
still refers to the original source class object (or the appropriate 
subobject thereof). [Note: that is, no copy is made. ]"

What sounds weird to me, but still implies the observed behavior.

[toc] | [prev] | [next] | [standalone]


#7547

FromJoe Greer <jgreer@doubletake.com>
Date2011-07-06 21:14 +0000
Message-ID<Xns9F1AAF6315183jgreerdoubletakecom@88.198.244.100>
In reply to#7527
Victor Bazarov <v.bazarov@comcast.invalid> wrote in news:iv1p6s$acb$1@dont-
email.me:

> 
>     void foo(const Foo* pFoo)
>     {
>        (pFoo ? *pFoo : Foo1()).bar(); // line 19 ************
>     }
> 

I wonder if it isn't the *pFoo that is doing the slicing since we are 
dealing with a type const Foo* in the argument list.

joe

[toc] | [prev] | [next] | [standalone]


#7657

FromVictor Bazarov <v.bazarov@comcast.invalid>
Date2011-07-08 15:56 -0400
Message-ID<iv7ndt$f33$1@dont-email.me>
In reply to#7547
On 7/6/2011 5:14 PM, Joe Greer wrote:
> Victor Bazarov<v.bazarov@comcast.invalid>  wrote in news:iv1p6s$acb$1@dont-
> email.me:
>
>>
>>      void foo(const Foo* pFoo)
>>      {
>>         (pFoo ? *pFoo : Foo1()).bar(); // line 19 ************
>>      }
>>
>
> I wonder if it isn't the *pFoo that is doing the slicing since we are
> dealing with a type const Foo* in the argument list.

Doing slicing where?  On its way into the 'foo' function?  Certainly 
not.  If I rewrite the ?: with 'if-else' syntax, I get the dynamic dispatch.

V
-- 
I do not respond to top-posted replies, please don't ask

[toc] | [prev] | [next] | [standalone]


#7729

FromJoe Greer <jgreer@doubletake.com>
Date2011-07-11 16:35 +0000
Message-ID<Xns9F1F801A05D6jgreerdoubletakecom@88.198.244.100>
In reply to#7657
Victor Bazarov <v.bazarov@comcast.invalid> wrote in
news:iv7ndt$f33$1@dont-email.me: 

> On 7/6/2011 5:14 PM, Joe Greer wrote:
>> Victor Bazarov<v.bazarov@comcast.invalid>  wrote in
>> news:iv1p6s$acb$1@dont- email.me:
>>
>>>
>>>      void foo(const Foo* pFoo)
>>>      {
>>>         (pFoo ? *pFoo : Foo1()).bar(); // line 19 ************
>>>      }
>>>
>>
>> I wonder if it isn't the *pFoo that is doing the slicing since we are
>> dealing with a type const Foo* in the argument list.
> 
> Doing slicing where?  On its way into the 'foo' function?  Certainly 
> not.  If I rewrite the ?: with 'if-else' syntax, I get the dynamic
> dispatch. 
> 
> V

[expr.cond]/3 applies in the case of a condition expression whereas it
doesn't for the if-else stmt.  The text is: 

    3 Otherwise, if the second and third operand have different types,
    and either has (possibly cv-qualified) class type, an attempt is made
    to convert each of those operands to the type of the other. The 
    process for determining whether an operand expression E1 of type T1 
    can be converted to match an operand expression E2 of type T2 is 
    defined as follows:
       — If E2 is an lvalue: E1 can be converted to match E2 if E1 can be
         implicitly converted (clause 4) to the type “reference to T2”, 
         subject to the constraint that in the conversion the reference 
         must bind directly (8.5.3) to E1.

       — If E2 is an rvalue, or if the conversion above cannot be done:
          — if E1 and E2 have class type, and the underlying class types 
            are the same or one is a base class of the other: E1 can be 
            converted to match E2 if the class of T2 is the same type as, 
            or a base class of, the class of T1, and the cv-qualification 
            of T2 is the same cv-qualification as, or a greater cv-
            qualification than, the cv-qualification of T1. If the 
            conversion is applied, E1 is changed to an rvalue of type T2 
            that still refers to the original source class object (or the 
            appropriate subobject thereof). [Note: that is, no copy is 
            made. ]

The way I read it is that since Foo1() is convertible to Foo, but not the 
otherway around, Foo1() will be converted/sliced to Foo then the bar() 
function will be called.

joe

[toc] | [prev] | [next] | [standalone]


#7733

FromVictor Bazarov <v.bazarov@comcast.invalid>
Date2011-07-11 13:05 -0400
Message-ID<ivfagb$7p5$1@dont-email.me>
In reply to#7729
On 7/11/2011 12:35 PM, Joe Greer wrote:
> Victor Bazarov<v.bazarov@comcast.invalid>  wrote in
> news:iv7ndt$f33$1@dont-email.me:
>
>> On 7/6/2011 5:14 PM, Joe Greer wrote:
>>> Victor Bazarov<v.bazarov@comcast.invalid>   wrote in
>>> news:iv1p6s$acb$1@dont- email.me:
>>>
>>>>
>>>>       void foo(const Foo* pFoo)
>>>>       {
>>>>          (pFoo ? *pFoo : Foo1()).bar(); // line 19 ************
>>>>       }
>>>>
>>>
>>> I wonder if it isn't the *pFoo that is doing the slicing since we are
>>> dealing with a type const Foo* in the argument list.
>>
>> Doing slicing where?  On its way into the 'foo' function?  Certainly
>> not.  If I rewrite the ?: with 'if-else' syntax, I get the dynamic
>> dispatch.
>>
>> V
>
> [expr.cond]/3 applies in the case of a condition expression whereas it
> doesn't for the if-else stmt.  The text is:
>
>      3 Otherwise, if the second and third operand have different types,
>      and either has (possibly cv-qualified) class type, an attempt is made
>      to convert each of those operands to the type of the other. The
>      process for determining whether an operand expression E1 of type T1
>      can be converted to match an operand expression E2 of type T2 is
>      defined as follows:
>         — If E2 is an lvalue: E1 can be converted to match E2 if E1 can be
>           implicitly converted (clause 4) to the type “reference to T2”,
>           subject to the constraint that in the conversion the reference
>           must bind directly (8.5.3) to E1.
>
>         — If E2 is an rvalue, or if the conversion above cannot be done:
>            — if E1 and E2 have class type, and the underlying class types
>              are the same or one is a base class of the other: E1 can be
>              converted to match E2 if the class of T2 is the same type as,
>              or a base class of, the class of T1, and the cv-qualification
>              of T2 is the same cv-qualification as, or a greater cv-
>              qualification than, the cv-qualification of T1. If the
>              conversion is applied, E1 is changed to an rvalue of type T2
>              that still refers to the original source class object (or the
>              appropriate subobject thereof). [Note: that is, no copy is
>              made. ]
>
> The way I read it is that since Foo1() is convertible to Foo, but not the
> otherway around, Foo1() will be converted/sliced to Foo then the bar()
> function will be called.

I think I get what you're saying.  In this particular case, since 'pFoo' 
was not null, the second part (E2) wasn't even evaluated.  The main 
driving thing in your analysis is that the common type is 'const Foo' 
(and not 'const Foo&' like one compiler used to think).  And it really 
comes from the definition of 'operator*' (earlier in the chapter 5).

V
-- 
I do not respond to top-posted replies, please don't ask

[toc] | [prev] | [next] | [standalone]


#7559

From"Paul" <pchristor@yahoo.co.uk>
Date2011-07-07 01:47 +0100
Message-ID<Pm7Rp.9$ux6.4@newsfe07.ams2>
In reply to#7527
"Victor Bazarov" <v.bazarov@comcast.invalid> wrote in message 
news:iv1p6s$acb$1@dont-email.me...
> Hello All,
>
> Here is the code:
>
>    class Foo {
>    public:
>       virtual void bar() const = 0;
>    };
>
>    #include <ostream>
>    #include <iostream>
>
>    class Foo1 : public Foo {
>       void bar() const { std::cout << "Foo1::bar\n"; }
>    };
>
>    class Foo2 : public Foo {
>       void bar() const { std::cout << "Foo2::bar\n"; }
>    };
>
>    void foo(const Foo* pFoo)
>    {
>       (pFoo ? *pFoo : Foo1()).bar(); // line 19 ************

What object is the argument pFoo pointing to?

>    }
>
>    void blah(const Foo& rFoo)
>    {
>       foo(&rFoo);
Is a temporary reference to a Foo on the call stack?
I think this is the case and there is no  Foo2 object.


>    }
>
>    int main()
>    {
>       blah(Foo2());

What is the lifespam of the temporary?
>    }
>
> Line 19 is the line in question.  Could you please interpret it for me? It 
> seems to skip the virtual function dispatch, and attempt to call the pure 
> function.  The common type of *pFoo and Foo1() is 'class Foo', and the 
> compiler seems to resolve the 'bar' statically, without the use of virtual 
> function mechanism, as if there is an instance of class Foo /sliced/ from 
> both original objects.  Is that supposed to happen? Standard chapter and 
> verse would be helpful.
>
> To avoid a pure function call I can implement the base class' member 
> function 'bar'.  The problem is that in that case I get the dynamic 
> dispatch I need (I expected Foo2.bar() to be called), I am getting the 
> base class.  Is that what *should* happen?
>
Havent debugged it but maybe it has something to do with the lifespam of the 
temporary. Have you tried it using a non temporary object?

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.c++


csiph-web