I don't recommend that you attempt to overload the stream extraction operator >>
in order to significantly change its behavior. It would make more sense to create your own function.
The stream extraction will accept user input such as "6sdfj23jlj"
as valid input for the number 6
. If you instead want to reject such input and reprompt the user for new input in such a case, then it would probably make more sense to use line-oriented input such as std::getline
, instead of the stream extraction operator >>
.
After reading in a string with std::getline
, you can attempt to convert it to an integer using the function std::stoi
.
In the following program, I have created a function get_int_from_user
, which will repeatedly ask the user for input, until the input is valid.
#include <iostream>
#include <string>
#include <cctype>
int get_int_from_user( const std::string& prompt )
{
std::string line;
std::size_t pos;
int i;
//repeat forever, until an explicit return statement or an
//exception is thrown
for (;;) //equivalent to while(true)
{
//prompt user for input
std::cout << prompt;
//attempt to read one line of input from user
if ( !std::getline( std::cin, line ) )
{
throw std::runtime_error( "unexpected input error!\n" );
}
//attempt to convert string to integer
try
{
i = std::stoi( line, &pos );
}
catch ( std::invalid_argument& )
{
std::cout << "Unable to convert input to number, try again!\n";
continue;
}
catch ( std::out_of_range& )
{
std::cout << "Out of range error, try again!\n";
continue;
}
//The remainder of the line is only allowed to contain
//whitespace characters. The presence of any other
//characters should cause the entire input to get rejected.
//That way, input such as "6sdfj23jlj" will get rejected,
//but input with trailing whitespace will still be accepted
//(just like input with leading whitespace is accepted by
//the function std::stoi).
for ( ; pos < line.length(); pos++ )
{
if ( !std::isspace( static_cast<unsigned char>(line[pos]) ) )
{
std::cout << "Invalid character found, try again!\n";
//we cannot use "continue" here, because that would
//continue to the next iteration of the innermost
//loop, but we want to continue to the next iteration
//of the outer loop
goto continue_outer_loop;
}
}
//input is valid
return i;
continue_outer_loop:
continue;
}
}
int main()
{
try
{
int i = get_int_from_user( "Please enter a number: " );
std::cout << "Input was ok, you entered: " << i << '\n';
}
catch ( std::runtime_error& e )
{
std::cout << "Runtime error: " << e.what() << '\n';
}
}
This program has the following behavior:
Please enter a number: Test
Unable to convert input to number, try again!
Please enter a number: 3000000000
Out of range error, try again!
Please enter a number: 6sdfj23jlj
Invalid character found, try again!
Please enter a number: 633245183
Input was ok, you entered: 633245183
The code above uses one goto
statement. Under most circumstances, you should avoid using goto
, but for breaking out of nested loops, it is considered appropriate.