I am trying to find the best way (i.e. more efficient and preferably more readable) of returning values from a function that has multiple return statements.
While playing a bit with different approaches I discovered three solutions:
test_func1
: creates a single lvalue in its scope, modifies it inside the branches and then returns it.test_func2
: creates an rvalue in the return statement of each branch and then returns it.test_func3
: creates an lvalue inside each branch and then returns it.
Here is the code (Compiler Explorerlink):
#include <iostream>#include <vector>struct MyStruct{ int m_value; std::vector<int> m_vec; MyStruct( const int value = 10 ) : m_value( value ), m_vec( { 1, 2, 3 } ) { std::cout << "ctor\n"; } MyStruct( const MyStruct& rhs ) : m_value( rhs.m_value ), m_vec( rhs.m_vec ) { std::cout << "copy ctor\n"; } ~MyStruct( ) { std::cout << "dtor\n"; }};// can be 1, 2 or 3// 1: generates the fewest lines of assembly// 2: ~45% more lines of assembly compared to 1// 3: ~240% more lines of assembly compared to 1#define FUNC_NUMBER 1#if FUNC_NUMBER == 1// create a single lvalue in the scope, modify it// inside the branches and then return itMyStruct test_func1( const int param ){ std::cout << param << ", Testing...\n"; MyStruct result; if ( param == 1 ) { result.m_value = 11; return result; } if ( param == 2 ) { result.m_value = 22; return result; } result.m_value = 33; return result;}#elif FUNC_NUMBER == 2// create an rvalue in the return statement of// each branch and then return itMyStruct test_func2( const int param ){ std::cout << param << ", Testing...\n"; if ( param == 1 ) { return MyStruct( 11 ); } if ( param == 2 ) { return MyStruct( 22 ); } return MyStruct( 33 );}#else// create an lvalue inside each branch and// then return itMyStruct test_func3( const int param ){ std::cout << param << ", Testing...\n"; if ( param == 1 ) { MyStruct result( 11 ); return result; } if ( param == 2 ) { MyStruct result( 22 ); return result; } MyStruct result( 33 ); return result;}#endifint main( ){ using std::cout;#if FUNC_NUMBER == 1 cout << test_func1( 3 ).m_value << '\n';#elif FUNC_NUMBER == 2 cout << test_func2( 3 ).m_value << '\n';#else cout << test_func3( 3 ).m_value << '\n';#endif}
Apparently, test_func1
and test_func2
have RVO applied (the constructor and the destructor are called only once). However this is not the case for test_func3
(in which the copy constructor is called too).
Why isn't the compiler able to generate smaller code for test_func2
and why does it generate a significant amount of more code for test_func3
? Is there any chance that GCC will become smarter in the future releases and generate the same code for all the three cases above?
Also, read the comments on lines 29-32.
===========================================================================================
Now the question is what is the most efficient way (copy-elision and move-elision) of returning values in such cases?