Как охватить все возможные типы данных при объявлении параметра функции? - PullRequest
1 голос
/ 23 октября 2010

Я пытаюсь создать функцию, которая будет выполнять проверку работоспособности ответа пользователя на ряд вопросов, каждый из которых в идеале должен быть ненулевым целым числом. Как я могу построить функцию, которая могла бы принимать параметр любого типа данных, но иметь только один параметр? Например:

bool SanityCheck(<type id> number)

где <type id> будет охватывать любой тип данных.

Ответы [ 7 ]

6 голосов
/ 24 октября 2010

Непонятно, что именно вы действительно хотите здесь.Неподтвержденный ввод от пользователя обычно происходит в форме строки.Как правило, вы читаете в строке, убедитесь, что она имеет желаемую форму (например, для целого числа, все цифры).Если он имеет правильную форму, вы преобразуете его в нужный тип и используете его.Если этого не произойдет, вы попросите пользователя повторно ввести свои данные, обычно с подсказкой типа «Пожалуйста, введите целое число от 1 до 10».

Шаблон функции является своего рода прямым ответом навопрос, который вы задали, но мне трудно представить, что он поможет вам в ситуации, описанной вами.Шаблон функции чаще всего используется в тех случаях, когда вам необходимо выполнить некоторые операции, которые синтаксически одинаковы для нескольких типов.Например, он позволяет добавлять два числа независимо от того, имеют ли они тип short, int, long, float, double, long double и т. Д. Это работает только потому, что они на самом деле являются всеми числами, и вы можете разумно использовать+ для добавления любого из них вместе.

Когда вы имеете дело с каким-то неизвестным вводом, это не относится, хотя - вам нужно проверить достаточно данных, чтобы убедиться, что операция разумна изначимым, прежде чем вы можете сделать с ним многое другое;довольно сложно получить значимый результат при сравнении (например) 7 с sunset.

3 голосов
/ 24 октября 2010

C ++ является статически типизированным языком.Тип переменной будет фиксированным во время компиляции и не может быть изменен во время выполнения.Однако то, что вводят пользователи, будет известно только во время выполнения и не может быть известно во время компиляции.Поэтому ваш вопрос не имеет смысла.


Когда вы ожидаете целое число от пользователя, тогда лучшим способом будет попытка прочитать целое число и проверить, успешно ли это:

int i;
std::cin >> i;
if(!std::cin)
  throw "Stupid user blew it!"; // or some real error handling

Однако, ловушкаэто означает, что после сбоя операции ввода поток ввода переходит в плохое состояние, и данные, которые не могут быть прочитаны, остаются во входном буфере.Если вы хотите обработать это изящно, придется очистить флаги состояния ошибки потока и заставить его игнорировать все, что находится во входном буфере.

Так что иногда может проще сначала прочитать строку

std::string input;
std::cin >> input;                   // either read up to any whitespace, or 
std::getline(std::cin, input);       // newline, or 
std::getline(std::cin, input, '\t'); // tab, or whatever you want

, потому что это всегда удается, а затем попытаться преобразовать ее в любые данные, которые вам нужны.Способ сделать это через строковые потоки:

std::istringstream iss(input);
int i;
iss >> i;

Теперь вы можете проверить состояние потока строки

if(!iss)

, и если преобразование не удалось, std::cin все еще будет использоваться иошибочный ввод прочитан из его буфера.

Однако есть еще одна загвоздка: если пользователь вводит «42 тысячи», то это не поймает ошибку.Остальные символы будут находиться во входном буфере потоков строк и будут игнорироваться.Поэтому для такого преобразования обычно требуется проверить, полностью ли прочитан буфер потока строки: чтение достигло EOF.Вы можете проверить это, вызвав iss.eof().Однако, если вы прочитаете целую строку , в конце может быть дополнительный пробел, который не должен приводить к сбою преобразования, поэтому вам нужно прочитать лишний пробел, прежде чем проверять EOF: iss >> std::ws.(std::ws - это потоковый манипулятор, который "съедает" последовательные пробелы.)
к настоящему времени преобразование будет выглядеть так:

std::istringstream iss(input);
int i;
iss >> i >> std::ws; // you can chain input
if(!iss.eof())
  throw invalid_input(input);

Конечно, это довольно сложно для одноразового использования.обращение и я бы точно не поклялся жизнью своих детей, что не осталось хорошего улучшения, о котором я еще не думал.Поэтому вы, по крайней мере, захотите обернуть это в функцию и поместить ее в свой набор инструментов для ее повторного использования (и исправления, если найдете ошибку):

bool convert_to_int(const std::string& str, int& result)
{
  std::istringstream iss(input);
  iss >> result >> std::ws;
  return iss.eof();
}

Или, универсальный для любого типа:

template< typename T >
bool convert_from_string(const std::string& str, T& result
{
  std::istringstream iss(input);
  iss >> result >> std::ws;
  return iss.eof();
}

Еще лучше было бы использовать для этого готовое готовое решение. Boost имеет именно такую ​​вещь со своим lexical_cast.


Вот скелетный алгоритм для всей процедуры ввода:

int i;
do {
    read string input
    convert to int i
while(!conversion succeeded);

С помощью битов, приведенных выше, вы сможете заполнить недостающие части.

2 голосов
/ 24 октября 2010

Хорошо, я думаю, что получил то, что вы на самом деле хотите сейчас.

Я полагаю, ваша ситуация выглядит примерно так:

  1. Чтение некоторого пользовательского ввода (возможно, с использованием std::cin).
  2. Убедитесь, что это int.
  3. Используйте int, если он один.

Если это так, то вам не нужна функция, которая может обрабатывать разные типы данных, потому что пользователь не может вводить разные типы данных, он может вводить только символы, и вам нужно выбрать, какой тип данных вы хотите сохранить как.

Я думаю, это то, что вам нужно:

bool valid = false;
int input = 0;
while (!valid)
{
  std::string inputStr;
  std::cin >> inputStr;
  valid = isInteger(inputStr);
  if (!valid)
    std::cout << "Please enter an integer." << std::endl;
  else
    input = atoi(inputStr.c_str());
}
std::cout << "You entered " << input << "!" << std::endl;

Вам придется написать isInteger самостоятельно, но, надеюсь, вы поймете идею.

2 голосов
/ 23 октября 2010

Использовать шаблоны:

template <typename T>
bool SanityCheck(T number);

Проверка работоспособности может различаться для разных типов.Поскольку это домашнее задание, я не буду больше публиковать код, только намекаю вам поисковым запросом Google «частичная специализация шаблона».

1 голос
/ 24 октября 2010

Вариант 1: используйте boost :: variable, если хотите, чтобы это была одна функция

Вариант 2: перегрузить эту функцию для всех типов, которые вам нужны

0 голосов
/ 24 октября 2010

Многие онлайн-опросы, которые меня просят заполнить, не просят меня вводить данные, а только выбирают вариант от 1 до 5. 1 = Полностью согласен, 5 = Полностью не согласен.Это кажется более эффективным способом сбора пользовательского ввода, поскольку у вас есть полный контроль над типом данных, и все, что мне нужно сделать, это выделить опцию.

0 голосов
/ 23 октября 2010

Сделав вашу функцию шаблонной, вы добьетесь этого.

template<typename T>
bool SanityCheck(T number);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...