Search code examples
c++return-value-optimization

Can I always rely on RVO by the compiler?


I have a function for reading files into a string variable. And I am not sure what is a better way to implement it:


            void readFile(const std::filesystem::path& path, std::string& dest)
            {
                std::ifstream input(path, std::ios::in | std::ios::binary);
                if (!input.is_open())
                {
                    // throw exception
                }
                const std::size_t size = std::filesystem::file_size(path);
                std::string().swap(dest);
                dest.resize(size);
                input.read(&dest[0], size);
                input.close();
                if (input.fail() && !input.eof())
                {
                    // throw exception
                }
            }

Or:

            std::string readFile(const std::filesystem::path& path)
            {
                std::ifstream input(path, std::ios::in | std::ios::binary);
                if (!input.is_open())
                {
                    // throw exception
                }
                const std::size_t size = std::filesystem::file_size(path);
                std::string buffer(size, '\0');
                input.read(&buffer[0], size);
                input.close();
                if (input.fail() && !input.eof())
                {
                    // throw exception
                }
                return buffer;
            }

The files to be read might be several bytes to several hundreds of megabytes in size, so read operations might be quite expensive. There are a lot of recommendations over the Internet to always prefer the second method using return value and just let the compiler do the optimizations needed. But can I completely rely on the compiler if efficiency matters for me? Can I be sure that the compiler would always prefer RVO to redundant copies of the data to be returned?


Solution

  • As mentioned by others, there is no guarantee that the compiler will optimize this, because the returned value is a named variable (see https://en.cppreference.com/w/cpp/language/copy_elision).

    However, in my opinion you can almost always rely on RVO if you enable optimizations, as RVO is one of the first and easiest optimization the compiler can do. The reason is, in the underlying architectures, there is no way to directly return a big value (bigger than a CPU register). The way it is done is that the caller function will allocate a local variable for the return value, and pass a pointer to this to the callee. So effectively, the compiler already has a local variable allocated on the stack, and will use this one instead of allocating another identical one. The compiler will construct your string in-place directly in the caller function local variable.