Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.c++ > #7527 > unrolled thread
| Started by | Victor Bazarov <v.bazarov@comcast.invalid> |
|---|---|
| First post | 2011-07-06 09:50 -0400 |
| Last post | 2011-07-07 01:47 +0100 |
| Articles | 11 — 6 participants |
Back to article view | Back to comp.lang.c++
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
| From | Victor Bazarov <v.bazarov@comcast.invalid> |
|---|---|
| Date | 2011-07-06 09:50 -0400 |
| Subject | Slicing 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]
| From | Michael DOUBEZ <michael.doubez@free.fr> |
|---|---|
| Date | 2011-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]
| From | Michael DOUBEZ <michael.doubez@free.fr> |
|---|---|
| Date | 2011-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]
| From | Bart van Ingen Schenau <bart@ingen.ddns.info> |
|---|---|
| Date | 2011-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]
| From | Victor Bazarov <v.bazarov@comcast.invalid> |
|---|---|
| Date | 2011-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]
| From | "Balog Pal" <pasa@lib.hu> |
|---|---|
| Date | 2011-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]
| From | Joe Greer <jgreer@doubletake.com> |
|---|---|
| Date | 2011-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]
| From | Victor Bazarov <v.bazarov@comcast.invalid> |
|---|---|
| Date | 2011-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]
| From | Joe Greer <jgreer@doubletake.com> |
|---|---|
| Date | 2011-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]
| From | Victor Bazarov <v.bazarov@comcast.invalid> |
|---|---|
| Date | 2011-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]
| From | "Paul" <pchristor@yahoo.co.uk> |
|---|---|
| Date | 2011-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