Как проверить тип данных в C ++?

Я довольно новичок в C ++, я в основном использую Python. Я пытаюсь проверить тип переменной значения, хранящегося в объектах, над которыми я работаю. Я помню, что в Python была команда isinstance где я мог бы использовать его как условие для запуска определенных команд, например, если следующее значение является строкой, сделать A, и если это int do B.

Есть ли способ быстро проверить, какой тип данных для переменной в C ++?

Пример:

В Python у меня был массив с математической операцией, каждый символ в поле

[3,"+",2]

когда я читаю массив, я отделяю int от строк с помощью команды isinstance

if isinstance(list[0],int):
aux1.append(list[0])
list=list[1:]
else:
if isinstance(lista[0],str):
aux2.append(list[0
list=list[1:]

теперь в C ++ мне нужно сделать что-то подобное, но на этот раз каждый символ находится в узле из связанного списка, и снова мне нужно отделить их, целые числа в связанном списке и строки в другом связанном списке

1

Решение

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

Прежде всего, вы должны быть уверены, что вам действительно нужно делать вещи так, как вы пытаетесь в настоящее время. Не пытайтесь писать код в стиле Python.

Тем не менее, есть в основном два разных подхода, с помощью которых вы можете добиться поведения, аналогичного тому, что позволяет вам делать Python (динамическая типизация, утка и, следовательно, относительно слабая типизация):

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

    struct Value {
    virtual void write_to(std::ostream &) const = 0;
    virtual void read_from(std::istream &) = 0;
    virtual ~Value() {} // Absolutely required!!!
    };
    struct Number : public Value {
    int data;
    void write_to(std::ostream & stream) const {
    stream << "<Number " << data << ">";
    }
    void read_from(std::istream & stream) {
    stream >> data; // Not the same format as write_to, shame on me
    }
    // Implicit destructor is fine
    };
    struct String : public Value {
    std::string data;
    void write_to(std::ostream & stream) const {
    stream << "<String " << data.size() << " " << data << ">";
    }
    void read_from(std::istream & stream) {
    stream >> data; // Not the same format as write_to, shame on me
    }
    };
    

    Используя это теперь вы можете, например, хранить Valueчей фактический тип вы можете позволить пользователю решить:

    std::vector<std::unique_ptr<Value>> values;
    while (wantsToEnterMoreValues) {
    std::string choice = ask("What type of value do you want to enter?");
    std::unique_ptr<Value> value;
    if (choice == "string") {
    value = std::make_unique<String>();
    } else if (choice == "number") {
    value = std::make_unique<Number>();
    } else {
    // launch apocalypse
    }
    value->read_from(std::cin);
    values.push_back(value);
    }
    

    Это легко расширяется на несколько типов. Обратите внимание, что для использования встроенной в C ++ динамической типизации вам нужно обходиться без семантики значений, но вместо этого полностью использовать семантику ссылок, либо с использованием реальных ссылок, либо (в большинстве случаев, когда право собственности должно быть передано, как в примере выше, в values вектор) с помощью указателей.

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

  2. Использовать union языковая особенность. Это стало действительно возможным только в C ++ 11, где члены объединения могут быть нетривиально выполнимыми:

    enum class Type {
    Number, String
    };
    struct Value {
    Type type;
    union {
    std::string string;
    int number;
    };
    Value(std::string const & s) : type(Type::String), string(s) {}
    Value(int n) : type(Type::Number), number(n) {}
    Value(Value const & v) : type(v.type) {
    switch (type) {
    case Type::Number: number = v.number; break;
    case Type::String: new (&string) std::string(v.string); break;
    default: break; // Launch nuclear missiles
    }
    }
    ~Value() {
    switch (type) {
    case Type::String: string.~std::string(); break;
    default: break;
    }
    }
    };
    

    Как видите, это довольно много работы. При таком подходе вы можете использовать семантику значений, но не можете так же легко расширить Values для поддержки большего количества типов. Кроме того, из-за использования union, ты собираешься потратить немного памяти.

Итог: вам нужно реализовать поведение самостоятельно, но вы можете сделать это именно так, как вы хотите, чтобы оно велось. Например: вы также можете реализовать операторы присваивания operator=(Value const &) которые делают неявные преобразования типов. Вы также можете использовать реализации из увеличение, лайк boost::any или же boost::variant,

Я хотел бы сослаться на два ответа, которые я написал на этом сайте, на одну и ту же тему, возможно, они также полезны для вас:

Также немного кода на C, который важен, потому что он пытается решить ту же проблему: https://stackoverflow.com/a/35443434/1116364

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

2

Другие решения

В отличие от Python, C ++ является строго типизированным языком. Это означает, что тип каждого объекта известен во время компиляции.

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

Если у вас есть указатель на объект, класс которого имеет хотя бы один виртуальный метод, dynamic_cast либо преобразует его в указатель на запрошенный класс, либо в nullptr, Это будет работать только тогда, когда наиболее производный объект, на который указывает указатель, однозначно включает оба класса в свою иерархию.

0

По вопросам рекламы [email protected]