C++ Function Parameter

This topic is 1905 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

Recommended Posts

hi,
i'm currently in practice with c++, i found a situation(not critical).

as i read from tutorial & article, it said that good thing in passing argument by reference, but not to change the value inside the function, is to use the constant-references as the function parameter.

in situation that i just want to pass the argument to a function just for resource only, not to change the value itself,
1. so when is the right time to use the by-value parameter ?
or
2. when is the time not to use the constant-references parameter ?

may anyone have suggestion..

thank you,
happy coding

Share on other sites

in situation that i just want to pass the argument to a function just for resource only, not to change the value itself,
1. so when is the right time to use the by-value parameter ?
or
2. when is the time not to use the constant-references parameter ?
The reason for using const references is to avoid the cost of performing expensive copy operations which occur when passing a complex type by value. Also, some classes outright prevent copying so you couldn't pass objects of those classes by value even if you wanted to. So, always use by-value for built-in types (ints, doubles, pointers, ...), and use const references for arguments with a class type. It's fine to pass simple, copyable classes by value also.

Share on other sites
yes, i also read about that too, use the by-val parameter for built-in types data, use the const-ref for class type data or large list/collection.

and additionally, a consideration when there are needs to copy that function param into local(of function block) variable, some suggested to provide the function with by-value param type.

that give me perspective now, thank you stroppy.

Share on other sites
Most of the time you'll pass by reference or pointer in methods, and then copy the value to a classes member variable if you want to hold onto that value.

for example a Vector3 class
[source lang="cpp"]

class Vector3
{
Vector3( double x, double y, double z )
: x(x),
y(y),
z(z)
{

}
double x;
double y;
double z;
};

class Actor
{
Vector3 mPosition;
void SetPosition( const Vector3 & pos )
{
mPosition = pos;
}
};

[/source]
this allows you to put in a temporary rvalue when you call the function

someActor.SetPosition( Vector3( 0.0, 0.0, 1.0) );

Which is passed by reference. The difference is that a reference is behind the scenes a pointer. A pointer being up to 64bits on 64bit os. Verses the size of the Vector3 being at least 3 * 64 ( if a double is 64 bits on your system ). This is a optimization. And since when the SetPosition class actually makes use of the reference it copies it, there is no worry about having a invalid reference. Edited by EddieV223

Share on other sites
I've posted my opinions on it in this thread ([size=2]I don't want to copy+paste the large-ish post).

Share on other sites

I've posted my opinions on it in this thread ([size=2]I don't want to copy+paste the large-ish post).
That's about return values, not parameters, though.

Regarding your post on that other thread, you claim that the compiler will use move semantics in a situation where a caller passes a (local?) value as a function parameter and doesn't use that value afterwards. I see how the compiler has a chance to optimize in this situation, but does the standard actually guarantee the compiler will do so? I don't remember reading about such a thing before. It seems like a potentially very expensive and difficult thing for the compiler to detect reliably under all conditions.

Share on other sites

[quote name='Servant of the Lord' timestamp='1351733904' post='4996031']
I've posted my opinions on it in this thread ([size=2]I don't want to copy+paste the large-ish post).
That's about return values, not parameters, though.

Regarding your post on that other thread, you claim that the compiler will use move semantics in a situation where a caller passes a (local?) value as a function parameter and doesn't use that value afterwards. I see how the compiler has a chance to optimize in this situation, but does the standard actually guarantee the compiler will do so? I don't remember reading about such a thing before. It seems like a potentially very expensive and difficult thing for the compiler to detect reliably under all conditions.
[/quote]
RVO and named RVO are both allowed by the standard. They are not REQUIRED by the standard, no optimizations of any kind are REQUIRED by the standard. Edited by Washu

Share on other sites

[quote name='Stroppy Katamari' timestamp='1351758851' post='4996112']
[quote name='Servant of the Lord' timestamp='1351733904' post='4996031']
I've posted my opinions on it in this thread ([size=2]I don't want to copy+paste the large-ish post).
That's about return values, not parameters, though.

Regarding your post on that other thread, you claim that the compiler will use move semantics in a situation where a caller passes a (local?) value as a function parameter and doesn't use that value afterwards. I see how the compiler has a chance to optimize in this situation, but does the standard actually guarantee the compiler will do so? I don't remember reading about such a thing before. It seems like a potentially very expensive and difficult thing for the compiler to detect reliably under all conditions.
[/quote]
RVO and named RVO are both allowed by the standard. They are not REQUIRED by the standard, no optimizations of any kind are REQUIRED by the standard.
[/quote]Hold on. I wasn't talking about RVO. In Servant of the Lord's example above we have the line:
std::vector<std::string> notACopy = GetStuff(veryLargeVector); //No copy made - not in passing the variable in, and not in return the variable.
For the return value, the standard guarantees that no copy happens. Either RVO is performed *or* the vector is move constructed. The thing I do not fully understand is the claim that veryLargeVector will not be copied when it goes in as a value parameter. Sure, a compiler might notice it will never be used again and use that to optimize (in spite of its lvalue-ness), but do we have an actual guarantee like we do with the return value? Edited by Stroppy Katamari

Share on other sites

[quote name='Servant of the Lord' timestamp='1351733904' post='4996031']
I've posted my opinions on it in this thread ([size=2]I don't want to copy+paste the large-ish post).
That's about return values, not parameters, though.[/quote]
The thread is about return values primarily, but my post talks about both parameters and return values.

Regarding your post on that other thread, you claim that the compiler will use move semantics in a situation where a caller passes a (local?) value as a function parameter and doesn't use that value afterwards. I see how the compiler has a chance to optimize in this situation, but does the standard actually guarantee the compiler will do so?

I don't remember reading about such a thing before. It seems like a potentially very expensive and difficult thing for the compiler to detect reliably under all conditions.[/quote]
I was under the impression from the Going Native 2012 videos that it is very likely to use move semantics in that situation, but I was getting confused between lvalues and rvalues. If it was an rvalue, such as an unnamed temporary, it would use move semantics instead of copying (if a move constructor exists for that type). I don't know if it's guaranteed that it will, but I think it would be if the argument type had the proper move constructor.

std::vector<std::string> result = GetStuff(std::vector<std::string>(1000, "very large string containing alot of data");

In this case, since the vector as an argument is an unnamed temporary, my possibly flawed understanding is that it will be guaranteed to be moved instead of copied (if a move constructor exists). Further, my (again: possibly-flawed) understanding that GetStuff(), without RVO, would create a temporary, and then the temporary would be moved into 'result'.

However, you could use std::move() to tell the compiler to move the lvalue and get the same effect: std::move() would take the lvalue and return it as a rvalue, allowing GetStuff() to use a move-constructor for its parameter.

std::vector<std::string> veryLargeVector; veryLargeVector.resize(2000, "very large string containing alot of data"); std::vector<std::string> result = GetStuff(std::move(veryLargeVector));

It seems all of this would depend on the class being passed around to actually implement move constructors though (which std::vector does).

Where I'm getting my impressions from:
[size=2]C++11: Rvalue references and move constructors (Wikipedia)
[size=2]A Brief Introduction to Rvalue References (2008)
[size=2]GoingNative 2012 - STL11: Magic && Secrets (2012 - Particularly 35 minutes into the video)

I haven't read the actual standard, despite having the PDF draft on my computer.

tl;dr: I was getting confused between lvalues and rvalues, but you can explicitly get the same result if you use std::move() and the type has a move constructor defined. Whether or not a compilers would automaticly detect it (which I recall hearing somewhere, but have no proof of, and it wouldn't be guaranteed), an explicit std::move() should guarantee it. Edited by Servant of the Lord

Share on other sites

tl;dr: I was getting confused between lvalues and rvalues, but you can explicitly get the same result if you use std::move() and the type has a move constructor defined. Whether or not a compilers would automaticly detect it (which I recall hearing somewhere, but have no proof of, and it wouldn't be guaranteed), an explicit std::move() should guarantee it.
Thank you, that was an excellent response. (A++++, would reply again, ... )
The Lavavej video at exactly 36:53 confirms my earlier point. In your initial example when you passed in a lvalue, you would have gotten a useless copy, contrary to what you initially assumed. I also think you are correct in that applying std::move to the argument would work here, assuming the type has a move constructor. However, the smart default to use is still const ref, and a beginner should just go with that every time instead of trying to puzzle out when to use by-value and when to use move. Moving the argument also leaves the undefined object dangling in the caller and causes the risk of accidentally using it again. I think it's safe to say >90% of people coding C++ for a living do not understand move yet, and a lot of them never will.

Share on other sites
Np, sorry for the initial confusion and spread disinformation.
[size=2]I edited the original post, not to change what I said, but to visually strike-out the wrong info and add a link to this thread.

The Lavavej video at ~35:00 indicates the proper time to use pass-by-value would be when you are creating a copy from the const ref anyway, because then with temporaries the copy can be avoided; as STL puts it in other places: "Never slower, but sometimes faster".

So, general rule of thumb seems to be:

• Default to const ref, unless...

• ...you need to modify the original, then default to regular ref
• ...you need to make a copy, then default to by-value which will sometimes (depending on move constructor) optimize away the copy for temporaries.

Which was pretty much the same rule of thumb C++ has always had.

Share on other sites
Read this (including the references -- Boris' three-parter "Efficient argument passing in C++11" is linked to in the blog post):
http://scottmeyers.blogspot.com/2012/10/parameter-types-in-constructors.html