All those experienced programmers who seem to know every nook and cranny of C++/C++11 are scary though <.< I didn't know while(std::cin>>string) was a thing, either, nor that you can make a 18 line program that counts unique words in a text file for you. Maybe I'm just lacking in experience ._.
Reading reference material and other people's code is the usual way I learn new tricks with C++. The trick that you mentioned with std::cin is due to the fact that operators are essentially functions with special calling syntax, combined with a special operator defined in the std::ios class (which std::istream, the class that std::cin is an object of, inherits from).
Part 1: Basic Operators
Operators are functions with special syntax and conventions on precedence. What does that mean? Well, let's say we wanted to add 2 to the product of 314 and 628. With normal functions, we'd have to do the order of operations ourself:
// plus(int, int) and multiplies(int, int) do exactly what you would expect
#include <iostream>
#include <functional> // plus(int, int) and multiplies(int, int) are functions in this header, in a slightly different form involving templates
using namespace std;
int main() {
cout << plus(2, multiplies(314, 628)); << endl;
return 0;
}
// Note: I could have written my own functions for adding and multiplying two integers, but the standard library provides them for me, so I decided to use them instead.
Sure, that's manageable now, but what if you had a bunch of operations strung together? Unless you're a LISP programmer, you probably don't want to keep up with all of those parentheses around the argument lists. What if you get one in the wrong place? If you're lucky, you'll get a wrong answer. If you're unlucky, your code won't compile. So, to make things more aesthetically pleasing in the code, we have these nice little things called operators.
#include <iostream>
using namespace std;
int main() {
cout << 2+314*628 << endl;
return 0;
}
That looks much nicer, and it has the added benefit of being identical to standard mathematical notation. But what if we had some fancy wrapper class for integers? If we wanted to use operators with that, we'd need to define our own. There's two methods of doing this, and of course there are people who will argue endlessly about which way is better. Let's look at one of them:
class SomeClass {
int A;
public:
SomeClass(int a) {
A = a;
}
SomeClass operator+(SomeClass other) {
return A + other.A;
}
};
We have to do this because the compiler isn't going to make assumptions about how user-defined classes should act with standard operators. No matter how annoying that may seem at times, it is certainly for a good reason. When a language guesses what you mean, you get... Well...
Anyways! Notice that the operator definition has a return type, just like a normal function. It evaluates just like a function, and returns a SomeClass object.
So that's the first part of how that trick works. But wait! std::operator>>(istream&, string&) returns an istream&, not a bool! Well...
Part 2: Special Operators and Casting
Yes, that's right. Operators can be used for casting. You read that right. In addition to the standard operators (+,-,*,/,%,^,&,|,&&,||,=,==,+=,-=,*=,/=,%=,&=,|=,^=,.,::,?:,&,..., did I miss any?), you can also define operators that control how an object of one class is casted to an object of a second class. The best part? That second class can be *any* class that can be constructed (aka not abstract), even user-defined classes. Let's look at an example:
class SomeClass {
int A;
public:
SomeClass(int a) {
A = a;
}
SomeClass operator+(SomeClass other) {
return A + other.A;
}
operator int() {
return A;
}
};
That looks just like the other operator definitions, only with 3 significant differences. First off, it does not accept any arguments. That's true for all casting operators. Second, the operator name is the name of the class that you are casting the object into. Third, there is no return type before the operator keyword. That's because of #2; the return type is in the definition as the operator name.
std::istream has an operator defined that casts objects to the void* type, returning a null pointer if none of the error flags are set (like the end of stream flag, which is set when the source stream runs out of data to be read), and a non-null pointer otherwise. It doesn't actually point to anything (in theory, it could, but the pointer is not meant to be dereferenced). Why have this? Well, pointers are implicitly castable to bools; a null pointer casts to false, and aany non-null pointer casts to true.
So there you go, an overly-long explanation for a simple trick. That little piece of code continually reads strings (actually C-style strings, not std::string's) in from standard input, stores them in the string variable, and returns its error state, which is evaluated by the while-loop to determine if it's alright to keep going.