53

Suppose I have a std::vector say Vector

Now after performing some operations on the vector(either insertion or deletion) I want to check if the vector is empty and on the basis of that I want to perform some operations.

Which approach is better

Approach 1

if (Vector.size() == 0){ /* operations */ }

Approach 2

if (Vector.empty()) { /* operations */ }

Which is a better approach, 1 or 2?

sbi
  • 219,715
  • 46
  • 258
  • 445
Prasoon Saurav
  • 91,295
  • 49
  • 239
  • 345
  • https://stackoverflow.com/questions/743197/size-vs-empty-in-vector-why-empty-is-preferred – x29a Mar 16 '15 at 13:55

8 Answers8

58

v.size() == 0 says "I'm comparing the size", but does so to check whether the container empty. There's a small algorithm to digest (very small, as it only consists of a comparison) before you know what it does.
OTOH, v.empty() does exactly what it says: it checks whether v is empty.
Due to this, I clearly prefer #2, as it does what it says. That's why empty() was invented, after all.

But there's also an algorithmic reason to prefer empty(): If someone later changes std::vector into a std::list, v.size() might have O(n). (In C++ 03 it's guaranteed to be O(1) for std::vector, but not for std::list. According to James' comment to Prasoon's answer it will be O(1) for all containers in C++1x.)

Community
  • 1
  • 1
sbi
  • 219,715
  • 46
  • 258
  • 445
  • 1
    In reply to your question/comment below, Howard Hinnant wrote up a whitepaper discussing the tradeoffs for `list` (it's actually a splice()-speed vs. size()-speed tradeoff). I can't seem to get to the whitepaper right now, but [Michael Burr summarized it.](http://stackoverflow.com/questions/228908/is-listsize-really-on/228914#228914). – James McNellis Oct 05 '10 at 11:57
  • @Donotalo: it's the new C++ standard that is going to be approved soon. For some time it was referred as "C++0x" (where 0x meant "in some year in range [2000, 2010)), but, since they didn't manage to get it approved before 2010, it's now also known as "C++1x". Some people still call it C++0x because it was called in that way or because they say that that "x" is to be intended as a hexadecimal digit. – Matteo Italia Oct 05 '10 at 12:57
  • @Donotalo: it's just that the original rationale for the name C++0x is no longer valid, so while most of us prefer to stick with the name C++0x to avoid confusion, a few people think the upcoming standard's nickname should be changed to C++1x even if it means no one knows what we're talking about. ;) To add some bonus confusion, C++0x, whatever we call it, is unlikely to be the only revision to the standard this decade. So if we start calling it C++1x, it might end up clashing with *another* C++1x, denoting the *next* standard revision. – jalf Oct 05 '10 at 14:46
  • As far as I'm concerned, the bottom line is this: C++0x is not an official name. It is a placeholder, a nickname used to identify the draft that is not yet standardized. As such it *doesn't need to be updated*. They could call it C++1643 and it'd work just as well. So I'm going to keep calling it C++0x until the day it is standardized and receives an official name. – jalf Oct 05 '10 at 14:47
  • @jalf: That x is a placeholder for a not yet fixed digit. If there should be another TR before 2020 (per ISO rules, a new version of the standard must not be released in less than ten years), the x in the standard we're now waiting for is long settled and there's a digit found for it. (Also, by your logic, C++0x should clash with C++03.) – sbi Oct 05 '10 at 18:47
  • @sbi: the `x` is a placeholder yes, but the name as a whole is also a placeholder, so it doesn't really matter what it *means*. And C++0x doesn't clash with C++03 because no one ever used the name C++0x to refer to C++03.;) Remember I'm talking about how the names are actually used in practice, and not what meaning was intended when they were coined. To my knowledge, the C++0x name has never been used to refer to anything other than what's most likely going to end up being C++11. C++1x is much less established, and *could* refer to "C++0x+1" as well as C++0x itself. – jalf Oct 06 '10 at 00:16
  • also to my knowledge ISO has no such rule. I'm not sure where that rumor comes from, but I've never seen it confirmed. And the existence of C++98 as well as C++03 seems to contradict it, as do official statements by committee members (including Stroustrup) saying they were hoping for something like a new standard every 5 years once C++0x is finalized. (I'd assume they would know about it if ISO prohibited such a release schedule) – jalf Oct 06 '10 at 00:19
  • The downside of C++1x is that you can pronounce C++0x "See-tox". – buildsucceeded May 31 '11 at 11:35
  • The problem of `empty()` is the English language. It is ambiguous whether "empty" is a verb or an adjective! – sergiol Feb 20 '19 at 16:25
14

Approach (2) would be better because empty() always runs in a constant time [i.e O(1)] irrespective of the container type.

size() too runs in O(1)[for std::vector] although it might run in O(n) for std:list [thats implementation defined to be honest]

In Effective STL[Item 4] Scott Meyers says

You should prefer the construct using empty, and the reason is simple: empty is a constant-time operation for all standard containers, but for some list implementations, size takes linear time.

.....

No matter what happens, you can't go wrong if you call empty instead of checking to see if size() == 0. So call empty whenever you need to know whether a container has zero elements.

Prasoon Saurav
  • 91,295
  • 49
  • 239
  • 345
  • 4
    No, `size()` is all but guaranteed to be O(1), especially for `vector`. In C++0x, `size()` will be required to be O(1) for any container that implements it. – James McNellis Oct 05 '10 at 11:48
  • 1
    @sbi: Yep; all containers. Currently the container requirements just say that `size()` _should_ have constant time complexity (which means that there isn't a complexity requirement at all). C++0x changes that. I was wrong in my first comment when I said "for any container that implements it," though; all containers must implement `size()`. – James McNellis Oct 05 '10 at 11:52
  • @James: Ah, didn't know that. I'll have to change my answer for that. Do you know what's the rationale for that change? It's a space-for-speed tradeoff, after all. – sbi Oct 05 '10 at 11:54
  • @JamesMcNellis: specially for `vector`?! – sergiol Feb 20 '19 at 16:26
8

I would say approch no 2, as method empty() was intentionally designed to check if an vector is empty. You may also check the efficiance of both approaches, and then decide which one is better.

Katalonis
  • 691
  • 2
  • 6
  • 16
  • 6
    The `empty` method is particularly good for `std::list`s because computing their size can be very.......long. – Benoit Oct 05 '10 at 11:48
  • 1
    @Benoit: Some implementations of `list` have a constant time `size()`; in C++0x, `size()` will be required to have constant time complexity for all containers, including `list`. – James McNellis Oct 05 '10 at 12:02
  • @James: Which is a good reason to use a custom or third-party `list`, if you happen to use `splice` and want *that* to be constant-time instead. Then, it's back to having `my_list::size` being O(N). – Potatoswatter Oct 05 '10 at 12:06
  • @James: Are you sure? As Potatoswatter said, this would require `splice` to be O(n), but I'm pretty sure it's required to be O(1). – Oliver Charlesworth Oct 05 '10 at 12:15
  • 1
    @Oli: Yes. Splicing a range of nodes from another list has linear time complexity (all other splices have constant time complexity). This complexity requirement is the same in both C++03 and C++0x. – James McNellis Oct 05 '10 at 12:17
  • @Oli: I meant the need for such a noncompliant list template doesn't go away just because the Standard doesn't provide it. @James: +1 – Potatoswatter Oct 05 '10 at 12:43
3

Typically a vector is internally implemented as a pointer to a dynamically allocated array,and data members holding the capacity and size of the vector. The size of the vector is the actual number of elements, while the capacity refers to the size of the dynamic array.

Given this implementation, the member function size() will simply be a getter to the member size.

The empty() will return the result of the comparison size == 0.

So both are equally efficient O(1) but its recommended to empty() if you want to check if vector is empty. Because that is what the function is there for. It'll make your code easier to read.

codaddict
  • 445,704
  • 82
  • 492
  • 529
2

Actually vector.empty() and vector.size()==0 are doing the same thing. empty compares the beginning and the end and returns true if they are the same, size calculates begin - end therefor returning 0 if it is empty therefor doing the same thing using another calculation.

Deathlymad
  • 41
  • 6
  • Sorry, can you rephrase your answer? It's difficult to understand, try separating the sentences and explicitly say why one is the same as the other. – Armfoot Jun 04 '15 at 19:36
  • That is not doing the same thing. The result is the same, but they are far from doing the same thing! – sergiol Feb 20 '19 at 16:27
2

If you are new to the programming, use one that has more meaning to you. For example if ==0 is more meaningful to you than .empty(), use that.

Later, if you have performance problems (which I strongly doubt that you will have here) use one that satisfies your performance targets.

Daniel Mošmondor
  • 19,718
  • 12
  • 58
  • 99
  • 5
    I'd rather my fellow-workers, newbies or not, use the one that has more meaning to experienced programmers. We'll all be experienced at some time, after all. – sbi Oct 05 '10 at 11:52
  • @sbi: Not only that, but the one that is better for performance in the long-run (e.g. if you switch from a `std::vector` to an `std::list` in the future). – Oliver Charlesworth Oct 05 '10 at 12:03
  • @sbi: you got your point there - however, code is hard to read, you have to make it easy for yourself here and now. – Daniel Mošmondor Oct 05 '10 at 12:07
  • @Daniel: We all agree on that. We just don't all agree on _what_ is easy to read. When I taught C++, my students sometimes used the most hilarious constructs because to them (with whatever background they brought with them) it seemed to be easier to read that way. Which is why I'm cautious about telling them to write it the way they find it easiest to read. – sbi Oct 05 '10 at 13:14
  • @sbi: some code, when read, feels right and you don't have to translate it in your mind. that is the code you should write for yourself. – Daniel Mošmondor Oct 05 '10 at 13:52
  • @Daniel: The key phrase in your reply is "write for yourself". I've always worked in a team, and worked with code that was maintained and extended for a decade or longer. When you write code for _that_, you write code for those looking at it the next decade and you should write code that's easiest to understand for _them_. Those "them" might be you, ten years from now, or it might be someone else. So it's best to stick to conventions that are acknowledged as universally as possible rather than to your current preferences. – sbi Oct 05 '10 at 14:07
  • One bad thing about `.empty()` is that it is not clear if it is an operation which will remove all the elements from the container, or if it will tell you if the container is empty or not. `isEmpty()` would be more my style. But this is the nature of the naming conventions in the standard library. And I'd have to get behind the "when in Rome..." rule; you should favor the better invariant for if the container changes. So someone going through a codebase and replacing all the `x.size() == 0` patterns with `x.empty()` would be justified--I'd argue it's the *right* answer *in this context*. – HostileFork says dont trust SE Jun 22 '14 at 11:53
1

Go for empty().

nakiya
  • 14,063
  • 21
  • 79
  • 118
1

Just for fun: why not:

if(Vector.begin() == Vector.end())

?

Benoit
  • 76,634
  • 23
  • 210
  • 236