#c
Вопрос:
Можете ли вы создать переменную, которая позволяет пользователю вводить данные любого типа, такие как int, строка … а затем программа выводит сообщение, в котором пользователю сообщается тип введенных им данных?
Комментарии:
1. Не уверен, чего ты действительно хочешь
std::variant
илиstd::any
что может помочь.2. Большую часть работы вам придется делать самому. Когда вы читаете входные данные, вы должны заранее решить, в какой переменной их хранить. Вы могли бы сделать что-то вроде сохранения входных данных в строке, а затем проверить «представляет ли это допустимое значение int? представляет ли это допустимый поплавок?» и т. Д., Но нет встроенного «какой тип соответствует этому входу».
3. Звучит так, как вы описываете
std::any
?4. как бы вы определили, хочет ли пользователь строку
"1"
1
, целое число или значение с плавающей запятой1f
? Когда пользователь только вводит1
, это может быть что угодно5. Прочитайте о том, что произошло, когда Excel попытался определить типы по входным данным.
Ответ №1:
Например, можно определить тип входных данных:
#include <initializer_list>
int main()
{
// std::initializer_list<int>
auto A = { 1, 2 };
// std::initializer_list<int>
auto B = { 3 };
// int
auto C{ 4 };
// C3535: cannot deduce type for 'auto' from initializer list'
auto D = { 5, 6.7 };
// C3518 in a direct-list-initialization context the type for 'auto'
// can only be deduced from a single initializer expression
auto E{ 8, 9 };
return 0;
}
Также можно ответить, какой тип данных был введен:
#include<iostream>
#include<string.h>
using namespace std;
int isint(char a[])
{
int len=strlen(a);
int minus=0;
int dsum=0;
for(int i=0;i<len;i )
{
if(isdigit(a[i])!=0)
dsum ;
else if(a[i]=='-')
minus ;
}
if(dsum minus==len)
return 1;
else
return 0;
}
int isfloat(char a[])
{
int len=strlen(a);
int dsum=0;
int dot=0;
int minus=0;
for(int i=0;i<len;i )
{
if(isdigit(a[i])!=0)
{
dsum ;
}
else if(a[i]=='.')
{
dot ;
}
else if(a[i]=='-')
{
minus ;
}
}
if(dsum dot minus==len)
return 1;
else
return 0;
}
int main()
{
char a[100];
cin>>a;
if(isint(a)==1)
{
cout<<"This input is of type Integer";
}
else if(isfloat(a)==1)
{
cout<<"This input is of type Float";
}
else
{
cout<<"This input is of type String";
}
}
Комментарии:
1. Почему половина этого ответа состоит из изображений кода?
2. Я полагаю, что когда OP говорит «введено пользователем», они имеют в виду ввод, считанный с консоли.
3. Использование
bool
вместоint
для возвращаемого типаisXXX
имело бы больше смысла.4. Я не думаю
3...---..5
float
, что это так .5. @NathanPierson, чем Натан этот пример является очень хорошим контрпримером и будет использоваться для улучшения кода
Ответ №2:
Не совсем.
При получении входных данных std::cin
с помощью функции ввода потока компилятор всегда должен знать тип входных данных.
напр.:
int a = 0;
std::cin >> a;
Это эффективно вызывает функцию с такой сигнатурой:
std::istreamamp; operator>>(std::istreamamp; stream, intamp; a);
Эта функция интерпретирует символы в потоке как целое число и пытается преобразовать эти символы в int
значение. (Что может быть успешным или неудачным, если входные данные на самом деле не представляют целое число).
Мы можем написать наши собственные функции, подобные приведенным выше, для преобразования символов во входном потоке в наши собственные пользовательские классы. Однако тип всегда должен быть известен во время компиляции.
Конечно, мы могли бы просто прочитать эту последовательность символов как строку (т. Е. Последовательность символов).
Затем мы могли бы написать нашу собственную логику для определения и преобразования типа значения, содержащегося в строке. (например, если оно содержится в кавычках, это строка. Если это только числа, то это целое число. Если это числа и десятичная дробь, то это число с плавающей запятой). Обратите внимание, что это может быть очень сложно , и трудно быть однозначным (например 12
, может быть интерпретировано как an int
, a float
или a double
или какой-либо пользовательский тип в зависимости от того, какие правила мы выбираем).
В качестве примера приведем правила о том, как C интерпретирует целочисленные литералы в коде.
Ответ №3:
Может ли переменная получать много типов данных?
Как бы. A std::variant
может содержать любой из нескольких предопределенных типов, например std::variant<int, double, float, Foo>
. Единственная проблема состоит в том, чтобы выяснить, какой тип ввести в него из вводимых пользователем данных.
Один из подходов может заключаться в том, чтобы прочитать строку в a std::string
и попытаться извлечь данные из этой строки во все типы, variant
пока это не удастся.
Для этого мы можем поместить string
в a std::istringstream
(который аналогичен, std::cin
но содержит только данные, которые вы в него вставили), а затем извлечь из istringstream
него переменную каждого типа в варианте. Как только кому-то удается istringstream
полностью истощить его, мы можем назвать это совпадением.
Если ни один из типов не совпадает, мы не хотим возвращать ничего, что может быть ошибочно принято за реальное значение, поэтому мы можем поместить значение variant
в a std::optional
, которое либо содержит значение, либо нет.
Чтобы помочь с извлечением из std::string
я создал шаблон функции под названием extract
параметры шаблона воз-это типы, которые он должен попытаться извлечь из string
. Он вернет a std::optional<std::variant<TheTypes...>>
с результатом.
namespace detail {
// a helper function that will try to extract into one type at a time recursively:
template<size_t I, class... Ts>
std::optional<std::variant<Ts...>> extract_helper(const std::stringamp; str) {
// the type to try to extract:
using value_type = std::tuple_element_t<I, std::tuple<Ts...>>;
std::istringstream is(str); // put the string in an istringstream
value_type var; // declare a varible of the current type
// try to extract into var and check that the istringstream is depleted:
if(is >> var amp;amp; !is.rdbuf()->in_avail()) return {std::move(var)};
// it wasn't value_type, try next if we've got more types to try:
if constexpr(I < sizeof...(Ts) - 1) return extract_helper<I 1, Ts...>(str);
return {}; // no type succeeded - return empty optional
}
} // namespace detail
// this the the function the user will use:
template<class... Ts>
std::optional<std::variant<Ts...>> extract(const std::stringamp; str) {
return detail::extract_helper<0, Ts...>(str);
}
С помощью этого вы можете позволить пользователю ввести что-то и проверить типы, которые вы предоставляете. Вы можете использовать std::visit
это, применяя вызываемый объект к тому variant
месту, где был разрешен правильный тип.
struct Line { // A std::string wrapper to extract a whole line
std::string str;
friend std::istreamamp; operator>>(std::istreamamp; is, Lineamp; l) {
return std::getline(is, l.str);
}
friend std::ostreamamp; operator<<(std::ostreamamp; os, const Lineamp; l) {
return os << l.str;
}
};
// helper type for visitor
template<class... Ts>
struct overloaded : Ts... {
using Ts::operator()...;
};
int main() {
for(std::string line; std::getline(std::cin, line);) {
// use the above `extract` function and try to match a few types:
auto opt = extract<long long, long double, std::string, Line>(line);
if(opt) { // if this is true, we got a match
autoamp; var = *opt; // a reference to the variant in the optional
// use visit with the visitor helper `overloaded` to get the type
// of input:
std::visit(overloaded{
[](long long) { std::cout << "integern"; },
[](long double) { std::cout << "floatn"; },
[](const std::stringamp;) { std::cout << "wordn"; },
[](const Lineamp;) { std::cout << "linen"; },
},
var);
} else {
std::cout << "Could not figure out what that wasn";
}
}
}
Это также будет работать для определяемых пользователем class
es, которые могут извлекаться из istream
s.
Комментарии:
1. С вводом, подобным
3
обоимint
, иdouble
будет совпадать (и пользовательский тип тоже может совпадать). Иstd::string
будет соответствовать чему угодно. Таким образом, это, похоже, вообще не идентифицирует пользовательский ввод.2. @user673679 Нужно будет указать типы в порядке приоритета, например
int
, перед adouble
иstring
последним, чтобы3
они были идентифицированы какint
и3.14
какdouble
и т.д., Но, конечно, это не идеально.
Ответ №4:
Не совсем точно, но, как я знаю, вы можете использовать шаблоны для чего-то подобного, или вы можете создать объект, который получает информацию и проверяет, является ли он целым числом, или плавающим, или строкой, или символом и т. Д. И сохранять различные временные переменные, не самый эффективный способ, лучше использовать шаблоны, но, по крайней мере, это решение.