Исходя из опыта работы с Python, я немного боролся с работой с типами в C ++.
Я пытаюсь инициализировать переменную класса через один из нескольких перегруженных конструкторов, которые принимают разные типы в качестве параметров. Я прочитал, что использование auto
ключевого слова может быть использовано для автоматического объявления переменной, однако в моем случае оно не будет инициализировано до тех пор, пока не будет выбран конструктор. Однако компилятор не рад тому, что он не инициализирован value
.
class Token {
public:
auto value;
Token(int ivalue) {
value = ivalue;
}
Token(float fvalue) {
value = fvalue;
}
Token(std::string svalue) {
value = svalue;
}
void printValue() {
std::cout << "The token value is: " << value << std::endl;
}
};
В Python это может выглядеть так:
class Token():
def __init__(self, value):
self.value = value
def printValue(self):
print("The token value is: %s" % self.value)
Как правильно использовать auto
ключевое слово в этом сценарии? Должен ли я использовать другой подход в целом?
auto
для учеников вообще? Актуальный, но устаревший вопрос: возможно ли иметь переменную-член auto?Ответы:
В C ++ нет такой вещи как «переменная неизвестного типа».
автоматически выводимые переменные имеют тип, который выводится из инициализатора. Если инициализатор отсутствует, вы не можете использовать авто. auto нельзя использовать для нестатической переменной-члена. Один экземпляр класса не может иметь членов с другим типом, чем другой экземпляр.
В этом сценарии нельзя использовать ключевое слово auto.
Вероятно. Похоже, вы пытаетесь реализовать
std::variant
. Если вам нужна переменная для хранения одного из X типов, это то, что вы должны использовать.Однако вы можете пытаться эмулировать динамическую типизацию в C ++. Хотя он может быть вам знаком по опыту работы с Python, во многих случаях это не идеальный подход. Например, в этом конкретном примере программы все, что вы делаете с переменной-членом, - это печатаете ее. Так что было бы проще хранить строку в каждом случае. Другие подходы - статический полиморфизм, как показано динамическим полиморфизмом в стиле Ратина или ООП, как показано в Fire Lancer.
источник
union
является механизмом низкого уровня, подверженным ошибкам.variant
вероятно, использует его внутри и делает его использование более безопасным.variant
делает использованиеunion
. Альтернатива, использующая необработанную память и размещение новой, не может быть использована вconstexpr
конструкторе.C ++ является статически типизированным языком , что означает, что все типы переменных определяются до времени выполнения. Следовательно,
auto
ключевое слово не является чем-то похожим наvar
ключевое слово в javascript, который является языком с динамической типизацией.auto
Ключевое слово обычно используется для указания типов, которые излишне сложны.Вместо этого вы можете использовать шаблонный класс C ++, который позволяет создавать несколько версий класса, которые принимают разные типы.
Этот код может быть ответом, который вы ищете.
Этот код компилируется, если выполняются некоторые условия, например, функция
operator<<
должна быть определена для std :: ostream & и типа T.источник
Другой подход, чем предложенный другими, заключается в использовании шаблонов. Вот пример:
Тогда вы можете использовать свой класс следующим образом:
источник
Вы можете использовать
std::variant
тип. Приведенный ниже код демонстрирует один из способов (но я должен признать, что он немного неуклюжий):Было бы намного лучше, если
std::get<0>(value)
бы их можно было записать как,std::get<value.index()>(value)
но, увы, «х» в<x>
должно быть константным выражением времени компиляции.источник
std::visit
вместоswitch
.auto
должен быть выводимым для определенного типа, он не обеспечивает динамическую типизацию во время выполнения.Если во время объявления
Token
вы знаете все возможные типы, которые вы можете использоватьstd::variant<Type1, Type2, Type3>
и т. Д. Это похоже на наличие «enum типа» и «union». Это гарантирует, что правильные конструкторы и деструкторы будут вызваны.Альтернативой может быть создание другого
Token
подтипа для каждого случая (возможно, с использованием шаблонов) с помощью подходящих виртуальных методов.источник
Решение, приведенное ниже, по духу аналогично тому, что было в ответе Огненного Лансера. Его ключевое отличие состоит в том, что он следует за комментарием, возможно, с использованием шаблонов , и, таким образом, устраняет необходимость явного создания производных экземпляров интерфейса.
Token
сам по себе не интерфейсный класс. Вместо этого он определяет интерфейс как внутренний класс и внутренний шаблонный класс для автоматизации определения производных классов.Это определение кажется слишком сложным. Однако
Token::Base
определяет интерфейс и являетсяToken::Impl<>
производным от интерфейса. Эти внутренние классы полностью скрыты от пользователяToken
. Использование будет выглядеть так:Кроме того, приведенное ниже решение иллюстрирует, как можно реализовать оператор преобразования для назначения
Token
экземпляра обычной переменной. Он полагаетсяdynamic_cast
и выдаст исключение, если приведение неверно.Определение
Token
ниже.Попробуйте онлайн!
источник