-2

I've met this code:

std::string str;
std::getline(std::cin, str);
std::string sub = str.substr(str.find('.') + 1);

first reaction was - this is invalid code. But after some thoughts it seems to be not so simple. So is it valid C++ expression (has predictable behavior)?

PS if it is not so clear question is mostly related to what will happen when '.' would not be found in str, but not limited to that, as there could be other issues.

LogicStuff
  • 19,397
  • 6
  • 54
  • 74
Slava
  • 43,454
  • 1
  • 47
  • 90
  • 1
    Was it literally as above, with the `...` in there?!? Or is the question about the `substr` line? – BoBTFish Dec 17 '15 at 15:54
  • It cannot be with `...` as it is invalid syntax. it means that does not matter what str is initialized with. Yes question is about line with `substr` – Slava Dec 17 '15 at 15:55
  • 1
    *First reaction was - this is invalid code.* Why? – Frédéric Hamidi Dec 17 '15 at 15:56
  • 1
    @Slava Well you were the one claiming you were surprised by obviously invalid code. The `substr` line doesn't look at all surprising to me. By using `...` instead of `"foobarbaz"`, you broke the cardinal rule and failed to provide an SSCCE. – BoBTFish Dec 17 '15 at 15:57
  • 2
    Did you even bother looking up the documentation? I don't understand why you felt the need to post this question. [`std::string::find`](http://www.cplusplus.com/reference/string/string/find/), [`std::string::substr`](http://www.cplusplus.com/reference/string/string/substr/) – Brian Rodriguez Dec 17 '15 at 15:58
  • @FrédéricHamidi question is what will happen when '.' not found – Slava Dec 17 '15 at 15:59
  • 1
    @Slava, then please go ahead and add this vital piece of information to your question. – Frédéric Hamidi Dec 17 '15 at 16:00
  • @FrédéricHamidi added that, thanks – Slava Dec 17 '15 at 16:02
  • @BrianRodriguez amount of answers and comments like if you can add 1 to `std::string::npos` shows that it is not obvious. – Slava Dec 17 '15 at 16:04
  • @Salvia Okay. Please be more explicit in the future, you could have avoided a lot of negative votes. – Brian Rodriguez Dec 17 '15 at 16:20
  • @BrianRodriguez make it more explicit it would be controversial. For example if I put `.` into string people would say it is correct because `.` is there. I thought it is pretty obvious what that code suppose to show. – Slava Dec 17 '15 at 16:29
  • @Slava The responses you've gotten clearly shows otherwise. If you had, instead of just puting `...`, given us a string without a `'.'`, your intention would have been crystal clear. You can't expect everyone to think the way you do, it's better to just say as much as possible and shorten it up later. – Brian Rodriguez Dec 17 '15 at 16:33
  • @BrianRodriguez missing . is only one issue. There could be more, for example . as a last symbol of the string. – Slava Dec 17 '15 at 16:44
  • @Slava No, that would have correct iterator behavior. If it were at the end of the string, adding one to it would return the same thing as `str.end()`. When two iterators are equal, that means it's an empty sequence, so you would get back an empty string. The only invalid form of this code is when the period doesn't exist at all, but the answer shows that even that too is well-formed, though by a hard-to-reason technicality. I would, personally, vote against using this in real-code. – Brian Rodriguez Dec 17 '15 at 16:49
  • @BrianRodriguez I understand that, point was issue in this code could be not only related to missing '.' so I did not want to ask "will it work fine when . is missing" but could be there any issues on valid input. – Slava Dec 17 '15 at 17:05

5 Answers5

5

str.find('.') returns the index of the first occurrence of the character in your string. substr with one argument returns the suffix of the string starting at the given index. So what the line does is it returns the tail of the string starting right after the first dot. So if str is "hello.good.bye", sub will be good.bye.

However, there is potentially a problem with the code if the string does not in fact contain any dots. It will return the whole string. This may or may not be intended. This happens because if there is no dot, find will return npos, which is the largest number std::string::size_type can hold. Add 1 to it, and you will get 0 (that's how unsigned types behave, modulo 2n).

So the code always has predictable behavior.

Armen Tsirunyan
  • 130,161
  • 59
  • 324
  • 434
2

From http://en.cppreference.com/w/cpp/string/basic_string/npos it seems that std::string::npos = -1;. That value is returned when the find is unsuccessful. In that case, the str.substr(0) will return the entire string.

It would seem it is valid and predictable code.

Anon Mail
  • 4,660
  • 1
  • 18
  • 21
2

If you're asking about what happens if . is not found, you don't have to worry. std::string::find then returns std::string::npos, which is defined to be -1 by the standard, which, after adding 1 overflows and makes the argument 0:

std::string sub = str.substr(0);

which gives you the whole string. I don't know if that's the desired behavior, but it's certainly not undefined behavior.

LogicStuff
  • 19,397
  • 6
  • 54
  • 74
1

Actually, in this specific case--because the string that contains "...does not matter..." actually does--the find() call is not going to find what it is looking for and so will return std::string::npos. You then add 1 to this.

npos is of type std::string::size_type, which is generally size_t, which is usually an unsigned integer of some sort.

npos is defined as the greatest possible value for size_type.

With size_type being unsigned adding 1 to the greatest possible value creates 0.

So you're calling std::string::substr(0). What does this do? It creates a copy of the whole string you called it on because substr takes a starting position and length (defaulting to npos, or "all the way to the end").

Edward Strange
  • 40,307
  • 7
  • 73
  • 125
0

As far as I can see it is valid though it isn't particularly readable.

If str is empty then the find() method will return std::string::npos. This is equivalent to the largest unsigned int representable by std::size_type. By adding 1 to this you're causing an integer overflow and it will wrap around to 0. This means the substr() method is attempting to create a string using chars from position 0 till the end of the string. If str is empty then sub is also empty.

Fibbs
  • 1,350
  • 1
  • 13
  • 23