473,396 Members | 1,770 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,396 software developers and data experts.

is it a good style to make a function return the referrence of one of its input parameters?

the code is like this:

class Asn_T
{
public:
DataType_T m_data;
};

class MyData
{
MyDataType m_data;
public:
Asn_T& Convert(Asn_T& asn)
{
// coverting codes goes here

return asn;
}
};

in a code review, some members of our team argued strongly that the
function MyData::Convert() should not return Asn_T&, instead, it should
be declared as returning void,because the return value is seldom used,
and it's enough to use the input parameter.

but i think this style of function declaration is more flexible, thus
is more reasonal.
i want to know your opinion about it. thanks

May 24 '06 #1
10 1626
steve yee wrote:
Asn_T& Convert(Asn_T& asn)


All the operator<<() methods overloaded for iostreams do that, as a
convenience for chaining. It's just a convenience.
foo(bar).baz().zone().etc().

For a more complex design, consider a function that sometimes returns its
input parameter, and sometimes returns something else.

--
Phlip
http://c2.com/cgi/wiki?ZeekLand <-- NOT a blog!!!
May 24 '06 #2
* steve yee:
the code is like this:

class Asn_T
{
public:
DataType_T m_data;
};

class MyData
{
MyDataType m_data;
public:
Asn_T& Convert(Asn_T& asn)
{
// coverting codes goes here

return asn;
}
};

in a code review, some members of our team argued strongly that the
function MyData::Convert() should not return Asn_T&, instead, it should
be declared as returning void,because the return value is seldom used,
and it's enough to use the input parameter.

but i think this style of function declaration is more flexible, thus
is more reasonal.
i want to know your opinion about it. thanks


Difficult to say without knowing more.

On the one hand, this technique is sometimes used to implement natural
chaining, as in the standard library's iostreams, or in the named
argument idiom*.

On the other hand, it's sometimes used to implement unnatural "concise"
notation that is difficult to decipher.

If had to guess, I'd guess the latter, because it's a reference to the
argument that's returned, not a reference to the object itself.

But on the third hand, returning a reference to the argument is exactly
what you do when implementing an iostream insertion operator, like

std::ostream& operator<<( std::ostream& stream, T const& obj )
{
return (stream << obj.asString());
}

And one certainly wouldn't want to establish a general principle that
would forbid iostream inserters.

*) <url: http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.18>

--
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?
May 24 '06 #3
steve yee wrote:
class Asn_T
{
public:
DataType_T m_data;
};

class MyData
{
MyDataType m_data;
public:
Asn_T& Convert(Asn_T& asn)
{
// coverting codes goes here

return asn;
}
};

in a code review, some members of our team argued strongly that the
function MyData::Convert() should not return Asn_T&, instead, it should
be declared as returning void,because the return value is seldom used,
and it's enough to use the input parameter.
Just so I understand correctly, the gist of your logic is to convert
the MyData object into an Asn_T, and store that result in the Asn_T
passed in by reference, right?
but i think this style of function declaration is more flexible, thus
is more reasonal.


"Flexible" is a funny word -- everyone has a different idea (or
several) about it. You see the above code as flexible because it gives
you two ways to get the return value. I would argue that it promotes
*inflexibility*, because it promotes inconsistency and an excessively
wide interface. You're providing two ways to do one thing, and
incurring the overhead of both. Being used in two different ways means
you've basically got two interfaces, which makes it harder to change
your code -- *that* is the kind of flexibility you should care about.
When debating high philosophical software design principles, always
remember *why* they're important, otherwise it's easy to fall into
traps like this.

That said, I'd implement it with what I consider the most
straightforward signature:
Asn_T MyData::Convert() const;

I made it const, because I don't think a member function called
Convert() would be reasonable if it modified *this. I prefer to simply
return by value, because it provides less opportunities to screw up
object lifetimes. I left out the parameter because it's unnecessary,
unless there's something you're not telling us about what this function
needs to do.

Luke

May 24 '06 #4
> Just so I understand correctly, the gist of your logic is to convert
the MyData object into an Asn_T, and store that result in the Asn_T
passed in by reference, right?
Yes.
That said, I'd implement it with what I consider the most
straightforward signature:
Asn_T MyData::Convert() const;
But this will cause overhead of temp object. You have to implement
MyData::Convert() this way:
Asn_T MyData::Convert()
{
Asn_T tempObject;
// convertin codes ...
...
return tempObject;
}
The construct/destruct/copy of tempObject will cause low effeceicy, do
you think so?
Being used in two different ways means
you've basically got two interfaces, which makes it harder to change
your code -- *that* is the kind of flexibility you should care about.


But such a simple function which does exactly only one thing
--converting itself to another type, is unlikely to change the type of
return value, nor is it neccessary. That is, the flexibility of
changing the code does not exist at all, so consideration should be
focused on the flexibility of the way to use the code.

BTW, how do you think of those ansi C functions which also return its
input parameters? such as: strcpy, memcpy, memmove, ...

May 24 '06 #5
steve yee <yi******@gmail.com> wrote:
Luke Meyers <n.***********@gmail.com> wrote:
That said, I'd implement it with what I consider the most
straightforward signature:
Asn_T MyData::Convert() const;


But this will cause overhead of temp object. You have to implement
MyData::Convert() this way:
Asn_T MyData::Convert()
{
Asn_T tempObject;
// convertin codes ...
...
return tempObject;
}
The construct/destruct/copy of tempObject will cause low effeceicy, do
you think so?


Not necessarily, look up RVO (return value optimization) and NRVO (named
RVO).

--
Marcus Kwok
Replace 'invalid' with 'net' to reply
May 24 '06 #6

steve yee wrote:
the code is like this:

class Asn_T
{
public:
DataType_T m_data;
};

class MyData
{
MyDataType m_data;
public:
Asn_T& Convert(Asn_T& asn)
{
// coverting codes goes here

return asn;
}
};

in a code review, some members of our team argued strongly that the
function MyData::Convert() should not return Asn_T&, instead, it should
be declared as returning void,because the return value is seldom used,
and it's enough to use the input parameter.

but i think this style of function declaration is more flexible, thus
is more reasonal.
i want to know your opinion about it. thanks


Chaining is one reason to do the above and another is to allow you to
portably return values without the cost of many copy operations.

void fun(const Ast_T & arg);

MyData d;
Ast_T t;

fun(d.Convert(t));

You can't do that if the function returns void. It is really what the
purpose of the argument is. If the argument is being passed in to
avoid temporaries then returning it as well is quite appropriate. For
instance, since you don't know how std::string works this is a good way
to return one - accept it as a parameter and return it too.

If the return value is seldom used however they have a point. You
don't want to encourage chaining except in very narrow situations. It
quite quickly reaches into the realm of unreadability.

May 24 '06 #7
steve yee wrote:
the code is like this:

class Asn_T
{
public:
DataType_T m_data;
};

class MyData
{
MyDataType m_data;
public:
Asn_T& Convert(Asn_T& asn)
{
// coverting codes goes here

return asn;
}
};

in a code review, some members of our team argued strongly that the
function MyData::Convert() should not return Asn_T&, instead, it should
be declared as returning void,because the return value is seldom used,
and it's enough to use the input parameter.
Several people have pointed that returning an Asn_T would allow
function chaining. However, the chaining idiom is usually seen using
the object on which the function was called

my_object.set_value(a).set_value(b).set_value(c);

not on one of its parameters.

IMO, either return an object or pass one by reference, but don't do
both.
but i think this style of function declaration is more flexible, thus
is more reasonal.


By "reasonal", do you mean "reasonable"? If so, something flexible is
not necessarily reasonable. There is a compromise to make between
flexibility (genericity) and usability.

If I understand your code correctly, Convert() converts the object to
an Asn_T. In this case, it makes more sense to create a new Asn_T
object and to return it by value. If I don't have an Asn_T prior to
calling Convert(), I must create one before.

void f(MyData& d)
{
Asn_T a;
d.Convert(a);
}

I read this as "I am creating an Asn_T and then using it to convert a
MyData". This implies default construction of an Asn_T (which could be
expensive) and is less clear semantically than

void f(MyData& d)
{
Asn_T a = d.Convert();
}

which I read as "I am creating an Asn_T specifically to convert a
MyData". Convert() should therefore look like

Asn_T MyData::Convert()
{
Asn_T a;
// ...

return a;
}
Jonathan

May 24 '06 #8
steve yee wrote:
the code is like this:

class Asn_T
{
public:
DataType_T m_data;
};

class MyData
{
MyDataType m_data;
public:
Asn_T& Convert(Asn_T& asn)
{
// coverting codes goes here

return asn;
}
};

in a code review, some members of our team argued strongly that the
function MyData::Convert() should not return Asn_T&, instead, it should
be declared as returning void,because the return value is seldom used,
and it's enough to use the input parameter.

but i think this style of function declaration is more flexible, thus
is more reasonal.
i want to know your opinion about it. thanks


Chaining is one reason to do the above and another is to allow you to
portably return values without the cost of many copy operations.

void fun(const Ast_T & arg);

MyData d;
Ast_T t;

fun(d.Convert(t));

You can't do that if the function returns void. It is really what the
purpose of the argument is. If the argument is being passed in to
avoid temporaries then returning it as well is quite appropriate. For
instance, since you don't know how std::string works this is a good way
to return one - accept it as a parameter and return it too.

If the return value is seldom used however they have a point. You
don't want to encourage chaining except in very narrow situations. It
quite quickly reaches into the realm of unreadability.

May 24 '06 #9
steve yee wrote:
That said, I'd implement it with what I consider the most
straightforward signature:
Asn_T MyData::Convert() const;
The construct/destruct/copy of tempObject will cause low effeceicy, do
you think so?


No, due to return value optimization performed by essentially all
modern C++ compilers. Really, though, that's besides the point.
Premature optimization is a bad idea in general -- that's not to say
you shouldn't try to write efficient code, but there is a line and this
is on the wrong side of it. Save this sort of micromanagement for
cases where you've discovered and measured a specific performance
problem associated with the code in question. Too many attempted
optimizations turn out to be useless, confusing, or actually less
efficient. Trust the compiler to do the right thing most of the time.
If you're concerned about performance, use a profiler.
Being used in two different ways means
you've basically got two interfaces, which makes it harder to change
your code -- *that* is the kind of flexibility you should care about.


But such a simple function which does exactly only one thing
--converting itself to another type, is unlikely to change the type of
return value, nor is it neccessary. That is, the flexibility of
changing the code does not exist at all, so consideration should be
focused on the flexibility of the way to use the code.


Why? Remember how I constrasted the kind of flexibility you're talking
about (multiple interfaces) with the kind of flexibility that's
important and helpful (making code easy to change/refactor). Whether
there is or is not a meaningful opportunity here to introduce the
latter, beneficial form of flexibility has nothing to do with implying
that the kind of flexibility you refer to is desirable in this or any
case. Excessively wide interfaces are problematic, in part because
they promote inconsistent use. Inconsistency makes code harder to
maintain because expectations tend to be incorrect -- read up on the
principle of least surprise.
BTW, how do you think of those ansi C functions which also return its
input parameters? such as: strcpy, memcpy, memmove, ...


Those interfaces were determined a long time ago, and a lot of software
engineering thinking has occurred in the mean time. I'm not saying
they're bad -- I don't use them frequently enough to have a strong
opinion on whether this practice is a good idea in those cases. I
wouldn't imitate them as a matter of course, though, by any means.

Luke

May 24 '06 #10

Luke Meyers wrote:
steve yee wrote:
That said, I'd implement it with what I consider the most
straightforward signature:
Asn_T MyData::Convert() const;


The construct/destruct/copy of tempObject will cause low effeceicy, do
you think so?


No, due to return value optimization performed by essentially all
modern C++ compilers. Really, though, that's besides the point.
Premature optimization is a bad idea in general -- that's not to say
you shouldn't try to write efficient code, but there is a line and this
is on the wrong side of it. Save this sort of micromanagement for
cases where you've discovered and measured a specific performance
problem associated with the code in question. Too many attempted
optimizations turn out to be useless, confusing, or actually less
efficient. Trust the compiler to do the right thing most of the time.
If you're concerned about performance, use a profiler.


There is a flip side in this case. If you do find that this is a
performance issue then you are going to be so tied to the interface
that getting out will be difficult at best. Interface decisions should
take performance in mind at least a little bit as it will be hard to
change them later.

May 24 '06 #11

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

Similar topics

30
by: Christian Seberino | last post by:
How does Ruby compare to Python?? How good is DESIGN of Ruby compared to Python? Python's design is godly. I'm wondering if Ruby's is godly too. I've heard it has solid OOP design but then...
5
by: Ben | last post by:
I have a form for data entry which is in a table. I have a select box to enter a customer name, which takes it's options from the customer database. I have a button to add a new customer. What I...
39
by: Mike MacSween | last post by:
Just spent a happy 10 mins trying to understand a function I wrote sometime ago. Then remembered that arguments are passed by reference, by default. Does the fact that this slowed me down...
28
by: Michael B. | last post by:
I tend to use rather descriptive names for parameters, so the old style of declaration appeals to me, as I can keep a declaration within 80 chars: void * newKlElem...
54
by: Sander | last post by:
1. I was having a discussion with somebody and I think it's just religious. We are developing some software and he was looking through my code. I use if (pointer) to test a pointer for NULL. He...
12
by: Newbie | last post by:
how can i call an oracle function to get data without using a select statement or stored procedures? given a project_no, i need to call the function: ops$sqltime.pa_new_job_no_fn which will...
0
by: Daniel Sélen Secches | last post by:
I found a good class to do a simple FTP. Very good.... I'm posting it with the message, i hope it helps someone ============================================================== Imports...
2
by: Jake Barnes | last post by:
Imagine I've this block of HTML: <p>Alex Schein Mailing List <input type="checkbox" name="newslettersToUse" value="133156"> (<a href="mcControlPanel.php"...
1
by: laila2ethan | last post by:
Need someones help , I am having a very difficult time understanding parameters and functions, can someone help me answer these questions 1. Write a program that helps an elementary school student...
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: 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...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
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
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
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
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...

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.