简体   繁体   中英

How do I only pay the cost of a function's created return value, when that return value is actually used?

Often times I see transformer functions that will take a parameter by reference, and also return that same parameter as a function's return value.

For example:

std::string& Lowercase(std::string & str){
    std::transform(str.begin(), str.end(), str.begin(), ::tolower);
    return str;
}

I understand that this is done as a convenience, and I am under the impression that the compiler will optimize for cases when the return value is not actually used. However, I don't believe the compiler can optimize for newly created return values of non-basic types. For example:

std::tuple<int,std::string,float> function(int const& num, std::string const& str, float const& f){

    return std::tuple<int,std::string,float>(num,str,f);
}

Constructors could do almost anything, and although the return type isn't used, it does not mean it would be safe to avoid creating the type. However, in this case, it would be advantageous to not create the type when the return value of the function isn't used.

Is there some kind of way to notify the compiler that if the return type is not being used, it's safe to avoid the creation of the type? This would be function specific, and a decision of the programmers; not something that the compiler could figure out on its own.

In the case of function and if the function is not inlined it might not optimize it since it has non trivial constructor . However, if the function is inlined it might optimize the unused return class if it's lifetime affects none of the arguments. Also since tuple is a standard type i believe most compiler will optimize that returned variable.

In general, there are two ways the compiler optimize your code:

  1. (Named) Return value optimization (RVO/NRVO)
  2. R-value reference

RVO

Take following code as example:

struct A {
    int v;
};

A foo(int v) {
    A a;
    a.v = v;
    return a;
}

void bar(A& a, int v) {
    a.v = v;
}

A f;
f = foo(1);

A b;
bar(b, 1);

In function foo , the variable a is constructed, modify its member v , and returns. In an human-optimized version bar , a is passed in, modified, and gives to caller.

Obviously, f and b are the same.

And if you know more about C++, you will know in returning from foo , result a is copied to outer f , and the dtor of a is called.

The way to make optimized foo into bar is called RVO , it omits the copy of internal a to outer f . Modification directly goes to variable of caller.

Please beware, there are cases RVO won't apply: copy ctor has side effect, multiple returns, etc.

Here is a detailed explaination:
MSDN
CppWiki

RValue

Another way to optimize return value is to use rvalue ctor introduced in C++ 11. Generally speaking, it's like calling std::swap(vector_lhs, vector_rhs) to swap the internal data pointer to avoid deep copy.

Here is a very good article about optimization: http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/

Last but not least

In Going Native 2013 , Andrei Alexandrescu gave a talk about how to write quick C++ code. And pass by reference is faster than rvalue . (Also RVO has some limitations) So if you do care about performance, please use pass by reference.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM