pochodzące głównie z pythonowego środowiska, z którym miałem problemy z pracą z typami w C ++.
Próbuję zainicjować zmienną klasy za pomocą jednego z kilku przeciążonych konstruktorów, które przyjmują różne typy jako parametry. Przeczytałem, że użycie auto
słowa kluczowego może być użyte do automatycznego zadeklarowania zmiennej, jednak w moim przypadku nie zostanie ono zainicjowane, dopóki nie zostanie wybrany konstruktor. Jednak kompilator nie jest zadowolony z braku inicjowania 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;
}
};
W Pythonie może to wyglądać następująco:
class Token():
def __init__(self, value):
self.value = value
def printValue(self):
print("The token value is: %s" % self.value)
Jaki jest właściwy sposób użycia auto
słowa kluczowego w tym scenariuszu? Czy powinienem zastosować zupełnie inne podejście?
auto
dla członków klasy? Istotne, ale nieaktualne pytanie: czy możliwe jest posiadanie zmiennej „auto” członka?Odpowiedzi:
W C ++ nie ma czegoś takiego jak „zmienna nieznanego typu”.
zmienne automatycznie wywnioskowane mają typ wywodzący się z inicjalizatora. Jeśli nie ma inicjalizatora, nie można użyć trybu automatycznego. Auto nie może być użyte dla niestatycznej zmiennej składowej. Jedna instancja klasy nie może mieć elementów o innym typie niż inna instancja.
W tym scenariuszu nie ma możliwości użycia słowa kluczowego auto.
Prawdopodobnie. Wygląda na to, że próbujesz wdrożyć
std::variant
. Jeśli potrzebujesz zmiennej do przechowywania jednego z X typów, powinieneś tego użyć.Być może próbujesz emulować dynamiczne pisanie w C ++. Chociaż może być ci znane z doświadczenia w Pythonie, w wielu przypadkach nie jest to idealne podejście. Na przykład w tym konkretnym programie przykładowym wszystko, co robisz ze zmienną składową, to wydrukuj ją. Dlatego łatwiej byłoby przechowywać ciąg w każdym przypadku. Inne podejścia to statyczny polimorfizm, jak pokazano za pomocą dynamicznego polimorfizmu w stylu Rhathin lub OOP, jak pokazano przez Fire Lancer.
źródło
union
jest podatnym na błędy mechanizmem niskiego poziomu.variant
prawdopodobnie używa go wewnętrznie i czyni korzystanie z niego bezpieczniejszym.variant
robi wykorzystaniaunion
. Alternatywy, wykorzystującej surową pamięć i nowe umieszczanie, nie można użyć wconstexpr
konstruktorze.C ++ jest językiem o typie statycznym , co oznacza, że wszystkie typy zmiennych są określane przed uruchomieniem. Dlatego
auto
słowo kluczowe nie jestvar
słowem kluczowym w javascript, który jest językiem dynamicznie wpisywanym.auto
słowo kluczowe jest powszechnie używane do określania typów, które są niepotrzebnie złożone.To, czego szukasz, można zrobić za pomocą klasy szablonów C ++, co pozwala na tworzenie wielu wersji klasy, która przyjmuje różne typy.
Ten kod może być odpowiedzią, której szukasz.
Ten kod skompiluje się, jeśli zostaną spełnione pewne warunki, takie jak funkcja
operator<<
powinna być zdefiniowana dla std :: ostream & i wpisz T.źródło
Innym podejściem, niż proponowali inni, jest używanie szablonów. Oto przykład:
Następnie możesz użyć swojej klasy w ten sposób:
źródło
Możesz użyć tego
std::variant
typu. Poniższy kod pokazuje jeden sposób (ale muszę przyznać, że jest trochę niezdarny):Byłoby znacznie ładniej, gdyby
std::get<0>(value)
można było napisać jako,std::get<value.index()>(value)
ale, niestety, „x”<x>
musi być wyrażeniem stałym w czasie kompilacji.źródło
std::visit
zamiastswitch
.auto
musi być możliwe do zdefiniowania dla określonego typu, nie zapewnia dynamicznego pisania w środowisku wykonawczym.Jeśli w momencie deklarowania
Token
znasz wszystkie możliwe typy, których możesz użyćstd::variant<Type1, Type2, Type3>
itp. Jest to podobne do posiadania „typu enum” i „unii”. Zapewnia to wywołanie odpowiednich konstruktorów i destruktorów.Alternatywą może być utworzenie innego
Token
podtypu dla każdego przypadku (ewentualnie przy użyciu szablonów) za pomocą odpowiednich metod wirtualnych.źródło
Poniższe rozwiązanie jest podobne pod względem duchowym do rozwiązania w Fire Lancer. Kluczową różnicą jest to, że podąża za komentarzem, prawdopodobnie używając szablonów , i tym samym eliminuje potrzebę jawnego tworzenia pochodnych instancji interfejsu.
Token
sama w sobie nie jest klasą interfejsu. Zamiast tego definiuje interfejs jako klasę wewnętrzną i wewnętrzną klasę szablonów do automatyzacji definicji klas pochodnych.Jego definicja wydaje się zbyt skomplikowana. Jednak
Token::Base
definiuje interfejs iToken::Impl<>
wywodzi się z interfejsu. Te wewnętrzne klasy są całkowicie ukryte dla użytkownikaToken
. Użycie wyglądałoby następująco:Ponadto poniższe rozwiązanie ilustruje sposób implementacji operatora konwersji w celu przypisania
Token
instancji do zmiennej regularnej. Opiera się na nimdynamic_cast
i zgłasza wyjątek, jeśli rzutowanie jest nieprawidłowe.Definicja
Token
jest poniżej.Wypróbuj online!
źródło