473,385 Members | 1,615 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,385 software developers and data experts.

Conditionally initializing a const reference without making a copy

Consider the classes Base, Derived1 and Derived2. Both Derived1 and
Derived2 derive publicly from Base.

Given a 'const Base &input' I want to initialize a 'const Derived1
&output'.
If the dynamic type of 'input' is Derived1, then 'output' should
become a reference to 'input'.
Otherwise 'output' should become a reference to the (temporary) result
of the member function 'input.to_der1()' which returns a Derived1
object by value.

For performance reasons the copy constructor of Derived1 must not be
called.

In trying to achieve this I used the following code:

Example #1:
const Derived2 *der2_ptr = dynamic_cast<const Derived2*>(&input);
const Derived1 &output =
(der2_ptr)?
der2_ptr->to_der1() :
static_cast<const Derived1 &>(input);

This works fine if 'der2_ptr != NULL', but when 'der2_ptr == NULL'
then Derived1's copy constructor is called (possibly because the
second and third argument of the conditional operator have to be
converted to the same type).

Alternatively I used the following code:

Example #2:
const Base &base =
(der2_ptr)?
der2_ptr->to_der1() :
input;
const Derived1 &output = static_cast<const Derived1&>(base);

This is even worse. It exposes a bug in g++ 3.4.x (the temporary
result of 'to_der1()' is destroyed before it has been used) and with g+
+ 4.x the copy constructor of Base (!) is called on the result of
'to_der1()' making the static_cast from 'base' to 'Derived1' invalid
(and still, a copy is made).

So why is this copy necessary? All the objects are there. It should be
possible to setup a reference to them without making a meaningless
copy.

G++ (incl. 4.x) even makes a copy in the following case:

Example #3:
Base my_base;
const Base &output =
(der2_ptr)?
der2_ptr->to_base() :
my_base;
The 'to_base' function returns a Base object by value.

I don't see why this copy is necessary while arguments 2 and 3 of
the ?: operator are both of type 'Base'.
I would say that at least in example #3 the situation is as described
by the C++ standard in 5.16:3 (first bullet). So either no conversions
or implicit conversions should occur. However, it looks like g++
treats the situation as in the second bullet, where a conversion
occurs by creating a temporary object.
Another possibility is that I am wrong and g++ is right because
paragraph 8.5.3:5 last bullet applies and the copy is 'necessary' to
initialize the reference (although it just looks like a waste to me).

My questions are:
- Why is the copy constructor called in the examples above? Is that
because of the standard (if so, what section) or is it because of g++?
- If the copies are necessary, is there another way to set up 'output'
without making copies?

Apr 11 '07 #1
10 3400
Ju**************@hotmail.com wrote:
Consider the classes Base, Derived1 and Derived2. Both Derived1 and
Derived2 derive publicly from Base.

Given a 'const Base &input' I want to initialize a 'const Derived1
&output'.
[.. copy is created when binding to a ref to const ..]

My questions are:
- Why is the copy constructor called in the examples above? Is that
because of the standard (if so, what section) or is it because of g++?
The Standard, 12.2. Don't use 'static_cast'. Try 'dynamic_cast'.
- If the copies are necessary, is there another way to set up 'output'
without making copies?
Use 'dynamic_cast'.

V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
Apr 11 '07 #2
On Apr 11, 4:11 pm, "Victor Bazarov" <v.Abaza...@comAcast.netwrote:
JurgenvonOert...@hotmail.com wrote:
Consider the classes Base, Derived1 and Derived2. Both Derived1 and
Derived2 derive publicly from Base.
Given a 'const Base &input' I want to initialize a 'const Derived1
&output'.
[.. copy is created when binding to a ref to const ..]
My questions are:
- Why is the copy constructor called in the examples above? Is that
because of the standard (if so, what section) or is it because of g++?

The Standard, 12.2. Don't use 'static_cast'. Try 'dynamic_cast'.
- If the copies are necessary, is there another way to set up 'output'
without making copies?

Use 'dynamic_cast'.

V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
The use of 'dynamic_cast' instead of the 'static_cast' does not make a
difference. It also wouldn't explain why copies are made in example #3.

Apr 11 '07 #3
Ju**************@hotmail.com wrote:
In trying to achieve this I used the following code:

Example #1:
const Derived2 *der2_ptr = dynamic_cast<const Derived2*>(&input);
const Derived1 &output =
(der2_ptr)?
der2_ptr->to_der1() :
static_cast<const Derived1 &>(input);

This works fine if 'der2_ptr != NULL', but when 'der2_ptr == NULL'
then Derived1's copy constructor is called (possibly because the
second and third argument of the conditional operator have to be
converted to the same type).
you're right, because the conditional operator requires the same type
for the last two arguments. What do you think about:

const Derived2 *der2_ptr = dynamic_cast<const Derived2*>(&input);
const Derived1 &output =
(der2_ptr)?
static_cast<const Derived1&>(der2_ptr->to_der1()) :
dynamic_cast<const Derived1 &>(input);

Alternatively I used the following code:

Example #2:
const Base &base =
(der2_ptr)?
der2_ptr->to_der1() :
input;
const Derived1 &output = static_cast<const Derived1&>(base);

This is even worse. It exposes a bug in g++ 3.4.x (the temporary
result of 'to_der1()' is destroyed before it has been used) and with g+
+ 4.x the copy constructor of Base (!) is called on the result of
'to_der1()' making the static_cast from 'base' to 'Derived1' invalid
(and still, a copy is made).
You may have to suggest the compiler the right conversion here as well.
>
So why is this copy necessary? All the objects are there. It should be
possible to setup a reference to them without making a meaningless
copy.
I think it's a matter of precedence in conversion rules for the
conditional operator, that are not always obvious. Anyway, I can't tell
you more without reading the standard.

- Why is the copy constructor called in the examples above? Is that
because of the standard (if so, what section) or is it because of g++?
I'm not able to answer properly to this question ^^ I should read the
standard :)
- If the copies are necessary, is there another way to set up 'output'
without making copies?
Yes, with a roughly and inaccurate explanation is the stuff that I wrote
before.

Regards,

Zeppe
Apr 11 '07 #4
Ju**************@hotmail.com wrote:
....
- If the copies are necessary, is there another way to set up 'output'
without making copies?
Yes. Try dealing exclusively with pointers and make a reference at the end.

Also, try posting a small compilable example so we can see exactly what
is happening.
Apr 11 '07 #5
Gianni Mariani wrote:
Ju**************@hotmail.com wrote:
...
>- If the copies are necessary, is there another way to set up 'output'
without making copies?

Yes. Try dealing exclusively with pointers and make a reference at the
end.
I'm not sure this is possible. It's possible to bind a temporary to a
reference to extend its lifetime, but if you take the pointer to that
temporary i think it's an error.

Regards,

Zeppe
Apr 11 '07 #6
Zeppe wrote:
Gianni Mariani wrote:
>Ju**************@hotmail.com wrote:
...
>>- If the copies are necessary, is there another way to set up 'output'
without making copies?


Yes. Try dealing exclusively with pointers and make a reference at
the end.


I'm not sure this is possible. It's possible to bind a temporary to a
reference to extend its lifetime, but if you take the pointer to that
temporary i think it's an error.
It seems to be that the OP has a problem with the temporary being
created in the first place.

Maybe I missed the point but a complete compilable example would help.
Apr 11 '07 #7
* Ju**************@hotmail.com:
Consider the classes Base, Derived1 and Derived2. Both Derived1 and
Derived2 derive publicly from Base.

Given a 'const Base &input' I want to initialize a 'const Derived1
&output'.
If the dynamic type of 'input' is Derived1, then 'output' should
become a reference to 'input'.
Otherwise 'output' should become a reference to the (temporary) result
of the member function 'input.to_der1()' which returns a Derived1
object by value.
You can't conditionally hold on to temporary.

But you can do

void foo( Derived1 const& o ) { ... }

void bar()
{
...
if( dynamic_cast<Derived1 const*>( &input ) )
{
foo( input );
}
else
{
foo( dynamic_cast<Derived2 const&>( input ).to_der1() );
}

It would probably be much better to make foo a virtual member function
of the base class.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
Apr 11 '07 #8
On Apr 11, 3:40 pm, JurgenvonOert...@hotmail.com wrote:
Consider the classes Base, Derived1 and Derived2. Both Derived1 and
Derived2 derive publicly from Base.
Given a 'const Base &input' I want to initialize a 'const Derived1
&output'.
If the dynamic type of 'input' is Derived1, then 'output' should
become a reference to 'input'.
Otherwise 'output' should become a reference to the (temporary) result
of the member function 'input.to_der1()' which returns a Derived1
object by value.
You want a reference which sometimes points to an existing
object, and sometimes to a temporary? In other words, you want
a reference for which the referred to object must sometimes be
destructed when the reference goes out of scope, and other times
not.
For performance reasons the copy constructor of Derived1 must not be
called.
That's a compiler detail. The copy constructor may always be
called.
In trying to achieve this I used the following code:
Example #1:
const Derived2 *der2_ptr = dynamic_cast<const Derived2*>(&input);
const Derived1 &output =
(der2_ptr)?
der2_ptr->to_der1() :
static_cast<const Derived1 &>(input);
This works fine if 'der2_ptr != NULL', but when 'der2_ptr == NULL'
then Derived1's copy constructor is called (possibly because the
second and third argument of the conditional operator have to be
converted to the same type).
Not just possibly. And it's not just a question of type. The
results of the ?: expression here is an rvalue---it can only be
an lvalue if both the second and the third arguments are
lvalues. You get an lvalue to rvalue conversion with the third
parameter. Which is a copy.
Alternatively I used the following code:
Example #2:
const Base &base =
(der2_ptr)?
der2_ptr->to_der1() :
input;
const Derived1 &output = static_cast<const Derived1&>(base);
This is even worse. It exposes a bug in g++ 3.4.x (the temporary
result of 'to_der1()' is destroyed before it has been used) and with g+
+ 4.x the copy constructor of Base (!) is called on the result of
'to_der1()' making the static_cast from 'base' to 'Derived1' invalid
(and still, a copy is made).
I'm not sure there's an error in g++ there, either. You can't
mix rvalues and lvalues in a ?: expression. If either of the
two expressions is an rvalue, the other will be converted to an
rvalue as well. And rvalues don't support polymorphism; an
rvalue has a specific type.
So why is this copy necessary?
In the end, because the compiler has to know whether to call the
destructor on what the reference refers to when the reference
goes out of scope. Here, because you can't have an ?:
expression that is sometimes an rvalue, and sometimes an lvalue.
All the objects are there. It should be
possible to setup a reference to them without making a meaningless
copy.
Al the objects aren't there. That's the problem. Sometimes,
the object is there, and sometimes it's not, and must be
created. And if it's created, it must be destructed. So the
answer is: create it every time.
G++ (incl. 4.x) even makes a copy in the following case:
Example #3:
Base my_base;
const Base &output =
(der2_ptr)?
der2_ptr->to_base() :
my_base;
The 'to_base' function returns a Base object by value.
Obviously. As soon as you have an rvalue, everything must be an
rvalue. Otherwise, you have serious lifetime of object issues.
I don't see why this copy is necessary while arguments 2 and 3 of
the ?: operator are both of type 'Base'.
If they're both lvalues, it isn't. If they're both rvalues, I
think the compiler may be able to skip the final copy as well
(but I wouldn't swear to it).
I would say that at least in example #3 the situation is as described
by the C++ standard in 5.16:3 (first bullet).
I don't see how. E1 (der2_ptr->to_der1(), in your first
example) cannot be converted to reference to T2 (i.e Derived1&).
So either no conversions
or implicit conversions should occur. However, it looks like g++
treats the situation as in the second bullet, where a conversion
occurs by creating a temporary object.
What else could it do? It tries conversion in both directions
(i.e. aligning E1 to the type of E2, and vice versa). The first
case fails, because E2 is an lvalue (point 1), and E1 cannot be
converted to a compatible lvalue. Inversing the roles of E1 and
E2, however, results in the second point matching.
Another possibility is that I am wrong and g++ is right because
paragraph 8.5.3:5 last bullet applies and the copy is 'necessary' to
initialize the reference (although it just looks like a waste to me).
My questions are:
- Why is the copy constructor called in the examples above? Is that
because of the standard (if so, what section) or is it because of g++?
- If the copies are necessary, is there another way to set up 'output'
without making copies?
The basic problem here is that the lifetime of the objects is
different. So you'll have to manage it yourself. Something
like:

Derived2 const* der2_ptr = dynamic_cast< Derived2 const*
>( &input ) ;
std::auto_ptr< Derived1* >
der1_ptr( der2_ptr == NULL
? NULL
: new Derived1( der2_ptr-
>to_der1() ) ) ;
Derived1 const& output( der2_ptr == NULL
? dynamic_cast< Derived1 const&
>( input )
: *der1_ptr ) ;

A more elegant solution might be based on boost::shared_ptr.
Base would contain a virtual function which returns a
boost::shared_ptr< Derived1 >: in Derived1, this function
constructs the results from this and a no-op deleter; in the
other classes, it constructs a Derived1 on the heap, and returns
a shared_ptr to the new object.

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Apr 12 '07 #9
On Apr 11, 6:15 pm, Zeppe
<zeppe.remove.all.this.long.comm...@email.itwrot e:
JurgenvonOert...@hotmail.com wrote:
In trying to achieve this I used the following code:
Example #1:
const Derived2 *der2_ptr = dynamic_cast<const Derived2*>(&input);
const Derived1 &output =
(der2_ptr)?
der2_ptr->to_der1() :
static_cast<const Derived1 &>(input);
This works fine if 'der2_ptr != NULL', but when 'der2_ptr == NULL'
then Derived1's copy constructor is called (possibly because the
second and third argument of the conditional operator have to be
converted to the same type).
you're right, because the conditional operator requires the same type
for the last two arguments. What do you think about:
const Derived2 *der2_ptr = dynamic_cast<const Derived2*>(&input);
const Derived1 &output =
(der2_ptr)?
static_cast<const Derived1&>(der2_ptr->to_der1()) :
dynamic_cast<const Derived1 &>(input);
I think that should compile. Of course, the temporary returned
by der2_ptr->to_der1() will be destructed at the end of the full
expression, so he'll end up with a dangling reference, but other
than that, it's fine.

The rules in the standard are somewhat complex. But there is a
reason behind them; in this case, the reason is linked to the
lifetime of the objects concerned.

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Apr 12 '07 #10
James Kanze wrote:
On Apr 11, 6:15 pm, Zeppe
>const Derived2 *der2_ptr = dynamic_cast<const Derived2*>(&input);
const Derived1 &output =
(der2_ptr)?
static_cast<const Derived1&>(der2_ptr->to_der1()) :
dynamic_cast<const Derived1 &>(input);

I think that should compile. Of course, the temporary returned
by der2_ptr->to_der1() will be destructed at the end of the full
expression, so he'll end up with a dangling reference, but other
than that, it's fine.
Uhm, are you sure about that? Because, according to the standard, a
temporary bounded to a reference should persist for the reference's
lifetime. The dangling pointer should appear if we go through pointers,
like:

const Derived1 &output =
*((der2_ptr)?
&der2_ptr->to_der1() :
&dynamic_cast<const Derived1 &>(input));

The rules in the standard are somewhat complex. But there is a
reason behind them; in this case, the reason is linked to the
lifetime of the objects concerned.
In my opinion in this case there is no problem with the lifetime.
Regards,

Zeppe

Apr 12 '07 #11

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

15
by: Wolfram Humann | last post by:
Hi, please don't be too harsh if I made stupid errors creating this simple example from my more complex case. Suppose I have a class like this: class BOOK { const string title;
19
by: Christian Engström | last post by:
If you have a function that returns something by value, the gcc compiler (version 3.2.3 on Windows XP with MinGW) converts the returned value from the type you specify in the code, to the const...
1
by: Thomas Matthews | last post by:
Hi, My goal is to extract a text field into a string type. The text field is from a buffer (array) of characters and delimited by white space or double quotes. *** I'm looking to perform this...
3
by: Zork | last post by:
Hi, I am a little confused with const functions, for instance (here i am just overloading the unary+ operator) if i define: 1) Length Length :: operator+ ( void ) const {return * this;} ... I...
6
by: Virendra Verma | last post by:
This sounds weird, but I am looking for separate behaviors for destruction of a const and non-const object. I am trying to develop a smart/auto pointer class for writing objects to disk...
12
by: cppaddict | last post by:
I'm confused about const references. I thought they provided a way to give access to private members without allowing those members to be changed. However, the following client code successfully...
17
by: My Name | last post by:
There was a topic on this earlier, but no answer, only people asking "Why do you want to do this..." Let's say I have a HUGE object and want to pass it to a routine that only needs read access...
11
by: u.int.32.t | last post by:
Is there a way to make a single template function that will accept both const and non-const parameter types by reference without making a copy? For example, I can write this which will accept...
14
by: Siegfried Heintze | last post by:
Why does VB.NET V2 force me to pass by value for my set function? When I try to change it to const byref it gives me a syntax error. It seems very inefficient to be passing strings around by value...
0
by: taylorcarr | last post by:
A Canon printer is a smart device known for being advanced, efficient, and reliable. It is designed for home, office, and hybrid workspace use and can also be used for a variety of purposes. However,...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: aa123db | last post by:
Variable and constants Use var or let for variables and const fror constants. Var foo ='bar'; Let foo ='bar';const baz ='bar'; Functions function $name$ ($parameters$) { } ...
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.