Может ли переменная получать много типов данных?

#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 , перед a double и string последним, чтобы 3 они были идентифицированы как int и 3.14 как double и т.д., Но, конечно, это не идеально.

Ответ №4:

Не совсем точно, но, как я знаю, вы можете использовать шаблоны для чего-то подобного, или вы можете создать объект, который получает информацию и проверяет, является ли он целым числом, или плавающим, или строкой, или символом и т. Д. И сохранять различные временные переменные, не самый эффективный способ, лучше использовать шаблоны, но, по крайней мере, это решение.