问题
I want to write a template function that does something with a std::stack<T>
and an instance of T
, e.g.:
template<class StackType> inline
bool some_func( StackType const &s, typename StackType::value_type const &v ) {
// ...
}
The reason I pass v
by reference is of course to optimize for the case where StackType::value_type
is a struct
or class
and not copy an entire object by value.
However, if StackType::value_type
is a "simple" type like int
, then it's of course better simply to pass it by value.
The question is: for a type such as int
that would become int const&
as a formal argument in the above function, will the compiler optimize away the reference and simply pass it by value?
回答1:
I look in gcc optimization options here http://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html
And actually there is an option for your case:
-fipa-sra
Perform interprocedural scalar replacement of aggregates, removal of unused parameters and replacement of parameters passed by reference by parameters passed by value.
Enabled at levels -O2, -O3 and -Os
As far as I know, -O2 is usual option for release build on linux.
So, the short answer is: one of good compiler does
回答2:
Though I haven't actually tested any compilers for this, I doubt it. You're assuming that passing a const reference is indistinguishable from just passing a value, but that's not true, and the compiler shouldn't assume that it is.
Since the reference is const, your function can't modify the value through it, but other parts of the code may have access to the original (non-const) variable, and might modify it. Your function might call some other function which changes it, for example, or there might be another thread running concurrently that does it.
If something else modifies that original variable, your function, with its reference, should see the new value. If the compiler replaced the reference with a copy, the function would still see the old value.
You might be interested in Boost's call traits library, though. It provides a template type call_traits<T>::param_type
which is a const reference for "big" types that you don't want to copy, and a value for "small" types where a copy would be more efficient. Basically, what you're wanting the compiler to do implicitly, you can do explicitly with your code.
回答3:
Whenever the compiler is able to inline, the resulting cross-procedural optimization will eliminate the expense of finding and passing an address.
For non-inline-able function calls, the reference probably will be implemented as a pointer, not pass-by-value.
回答4:
This is MSVC2010 64bit compiler, full optimizations:
int foo( int i )
{
int a = i + i;
return ( a );
}
//Disassembly:
//00000001`3ff11c50 8d0409 lea eax,[rcx+rcx]
//00000001`3ff11c53 c3 ret
int bar( int const & i )
{
int a = i + i;
return ( a );
}
//Disassembly:
//00000001`3ff11c10 8b01 mov eax,dword ptr [rcx]
//00000001`3ff11c12 03c0 add eax,eax
//00000001`3ff11c14 c3 ret
In the first case, value is passed on RCX
, in the second - address passed on RCX
. Of course, if functions are inlined, as Ben said, then the produced code might be absolutely the same in both cases.
Passing instances of "small" classes (say, class only has an int
data member and trivial constructor, bitwise copy-constructor etc) is faster by value. Compiler will optimize it as, essentially, just passing a copy of int
. So, you can overload your function, as Wyzard pointed out, using some type traits. Something like that:
template< typename PType >
PType Func( typename std::enable_if< ( sizeof( PType ) > sizeof( size_t ) ), PType const & >::type f )
{
//large things: reference
}
template< typename PType >
PType Func( typename std::enable_if< ( sizeof( PType ) <= sizeof( size_t ) ), PType >::type f )
{
//small things: value
}
来源:https://stackoverflow.com/questions/9014021/will-a-good-c-compiler-optimize-a-reference-away