问题描述
我有一个将文件读入字符串变量的函数。而且我不确定什么是更好的实现方式:
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
}
}
或者:
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;
}
要读取的文件大小可能是几个字节到几百兆字节,因此读取操作可能会非常昂贵。互联网上有很多建议总是更喜欢使用返回值的第二种方法,让编译器进行所需的优化。但是,如果效率对我很重要,我可以完全依赖编译器吗?我可以确定编译器总是更喜欢 RVO 而不是要返回的数据的冗余副本吗?
解决方法
正如其他人所提到的,不能保证编译器会对此进行优化,因为返回值是一个命名变量(请参阅 https://en.cppreference.com/w/cpp/language/copy_elision)。
但是,在我看来,如果启用优化,您几乎总是可以依赖 RVO,因为 RVO 是编译器可以执行的第一个也是最简单的优化之一。原因是,在底层架构中,没有办法直接返回一个大值(比CPU寄存器大)。这样做的方法是调用者函数将为返回值分配一个局部变量,并将指向 this 的指针传递给被调用者。如此有效,编译器已经在堆栈上分配了一个局部变量,并将使用这个变量而不是分配另一个相同的变量。编译器将直接在调用函数局部变量中就地构造您的字符串。
,我有一个将文件读入字符串变量的函数。而且我不确定什么是更好的实现方式:
[...]
...但是如果效率对我来说很重要,我可以完全依赖编译器吗? ...
编写代码的第一条规则是让人类读者清楚。 “过早优化是万恶之源。”一旦你有了清晰的代码,就剖析,看看你的性能问题是否真的像你认为的那样;你可能会感到惊讶。
大多数开发者可能会发现
std::string readFile(const std::filesystem::path& path) { /* ... */ }
比使用“out”参数更清晰。 (如果文件读取性能真的那么重要,那么您可能想要的不是同步读取 std::ifstream
到 std::string
。)