哪个更快,string.empty() 还是 string.size() == 0?

20

最近,与一位同行程序员进行讨论时,有人要求我进行一些代码更改。我的代码大致如下:

if( mystring.size() == 0)
    // do something
else
    // do something else 

讨论的是使用mystring.empty()来验证字符串是否为空。我同意,string.empty()可能更易读,但这样做会带来性能方面的好处吗?

我查找了两个与我的问题相关的答案:

这两个答案都支持我的看法,即string.empty()仅仅是更易读,并没有相对于string.size() == 0带来任何性能优势。

我仍然想确认一下,是否有任何实现string的方法来保持一个内部布尔标志以验证字符串是否为空?还是有其他某些实现方法可以否定我的说法?


@chris:我同意你的看法,老兄。只是想确认一下。 - Jatin Ganhotra
13
我的观点是,string.empty()只是更易读,并没有提供任何性能优势。"just"一词的意思是仅仅、只不过。在编程中,可读性是首要考虑因素,而性能尤其是在这个层面上,远远落后。即使有string的实现,也只是将一个值与零进行比较,就像size == 0一样。 - Jim Balter
@JimBalter:我也认为代码可读性非常重要,我只是想从这里的人们那里获得一些意见,关于empty()的底层实现是否比size()更快? - Jatin Ganhotra
@所有人:我不明白为什么要给我点踩。我已经非常清楚地表达了问题,而且这是一个有效的问题。 - Jatin Ganhotra
除非问题被编辑,否则无法删除踩。 - Jim Balter
@JimBalter,真的吗?那很有趣。里面有一个短的撤回超时吗? - chris
2个回答

35
标准将empty()定义如下:

bool empty() const noexcept;
返回: size() == 0.

很难找到不遵循此标准的内容,由于两者都是恒定时间操作,因此任何性能差异都可以忽略不计。在可读性方面,empty()明确清晰,应优先考虑使用它而不是size() == 0(或!size())。

同意empty()更清晰易懂。但在某些情况下,size()和empty()之间存在差异。在msvc 2015中进行测试:对于std::string empty(),它确实是size()==0,而string::size()返回一个缓存的大小(因此没有除法)。但对于vector<int>::empty(),它执行first()==last(),而vector<int>::size()则执行隐式除法的减法操作!在这种特定情况下,使用位移。因此,在向量的情况下,empty()仅执行比较并避免执行该除法。实际上,在大多数情况下,这不会产生任何明显的差异 :) - kalmiya
1
@kalmiya,有趣的是,我注意到Clang生成了几乎相同的汇编代码(https://godbolt.org/g/821yyw)。 - chris

7
现在,这是一个相当琐碎的问题,但我会尽力详细解释,以便同事们提出的任何论点都不会让你惊讶...通常情况下,如果分析证明你确实需要关注,那就进行测量:可能存在差异(见下文)。但是,在一般的代码审查情况下,对于未被证明存在问题的缓慢代码,突出的问题是:
- 在某些其他容器中(例如C++03的列表,但不包括C++11),size()比empty()效率低,因此有一些编码提示建议在一般情况下优先使用empty()而不是size(),以便如果有人稍后需要切换容器(或将处理通用化为模板,其中容器类型可能会变化),则无需更改即可保持效率。 - 两者哪个反映了更自然的测试方式?-不仅仅是你首先想到的,或者是size(),因为你没有习惯使用empty(),而是当你100%专注于周围的代码逻辑时,size()或empty()更适合?例如,也许因为它是size()的几个测试之一,你喜欢保持一致性,或者因为你正在实现一个著名的算法或公式,它通常用size表示:保持一致性可能会减少验证实现与公式之间的心理噪音/努力。
大多数情况下,上述因素都是微不足道的,在代码审查中提出这个问题实际上是浪费时间。
可能的性能差异
虽然标准要求功能等效,但某些实现可能会以不同的方式实现它们,尽管我已经努力并且到目前为止未能记录一个特别合理的原因来做到这一点。
C++11对其他影响实现选择的函数的行为有更多的约束:data()必须以NUL结尾(曾经只是c_str()),[size()]现在是有效索引,并且必须返回对NUL字符的引用。由于各种微妙的原因,这些限制使得empty()比size()更不可能更快。
无论如何-如果你必须关心,请进行测量。

2
如果你的 empty 函数在字符串开头检查 NUL,那么它是不正确的,因为 std::string 允许 NUL 字符,而 C 字符串则不允许。 - rici
在C++11中,字符串内容必须以NUL结尾。但是@rici是正确的;检查第一个字符是否为NUL的实现将是错误的。 - Jim Balter
@rici:讽刺的是...我找到了另一个关于空字符串检测的问题链接,刚刚完成了对别人答案中关于NUL的确切问题的解决,结果发现我自己也忽略了它。干杯。 - Tony Delroy
@TonyD:我认为nullptr也不会起作用,因为即使是空字符串也需要以NUL结尾,并且如果通过data()成员函数访问,则NUL需要具有可解引用的地址。我不确定在每次调用data()(可能还包括每次调用begin()/end())时添加对nullptr的测试是否被描述为“模糊合理”。 - rici
@rici:好的-我相信没有一个理智的实现会这样做,而且仔细思考后,显而易见的SSO实现可能会让您检查end_是否指向内部缓冲区的第一个字符,这将是从this开始的固定偏移量,但它只比与begin_进行比较少了一次间接引用。关于实际实现的内容-我大约在12个月前看过Visual C++和GCC,我的回忆是它们都存储beginend-惯用迭代似乎比size()更受重视,这对我来说很公平。 - Tony Delroy
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接