Ich versuche, auf den Inhalt einer Variante zuzugreifen. Ich weiß nicht, was da drin ist, aber zum Glück tut es die Variante. Also dachte ich, ich frage die Variante einfach, auf welchem Index sie sich befindet, und verwende diesen Index dann für std::get
ihren Inhalt.
Dies kompiliert aber nicht:
#include <variant>
int main()
{
std::variant<int, float, char> var { 42.0F };
const std::size_t idx = var.index();
auto res = std::get<idx>(var);
return 0;
}
Der Fehler tritt beim std::get
Aufruf auf:
error: no matching function for call to ‘get<idx>(std::variant<int, float, char>&)’
auto res = std::get<idx>(var);
^
In file included from /usr/include/c++/8/variant:37,
from main.cpp:1:
/usr/include/c++/8/utility:216:5: note: candidate: ‘template<long unsigned int _Int, class _Tp1, class _Tp2> constexpr typename std::tuple_element<_Int, std::pair<_Tp1, _Tp2> >::type& std::get(std::pair<_Tp1, _Tp2>&)’
get(std::pair<_Tp1, _Tp2>& __in) noexcept
^~~
/usr/include/c++/8/utility:216:5: note: template argument deduction/substitution failed:
main.cpp:9:31: error: the value of ‘idx’ is not usable in a constant expression
auto res = std::get<idx>(var);
^
main.cpp:7:15: note: ‘std::size_t idx’ is not const
std::size_t idx = var.index();
^~~
Wie kann ich das beheben?
Antworten:
Im Wesentlichen können Sie nicht.
Sie schrieben:
... aber nur zur Laufzeit, nicht zur Kompilierungszeit.
Und das bedeutet, dass Ihr
idx
Wert keine Kompilierungszeit hat.Und das bedeutet, dass Sie nicht
get<idx>()
direkt verwenden können.Sie können eine switch-Anweisung verwenden. hässlich, aber es würde funktionieren:
Das ist jedoch ziemlich hässlich. Wie aus den Kommentaren hervorgeht, können Sie
std::visit()
den Wechsel auch ganz vermeiden (was sich nicht wesentlich vom obigen Code unterscheidet, außer dass Sie verschiedene Vorlagenargumente verwenden, anstatt dies explizit zu tun). Weitere indexbasierte Ansätze (nicht spezifisch fürstd::variant
) finden Sie unter:Redewendung zur Simulation numerischer Laufzeitvorlagenparameter?
quelle
Der Compiler muss den Wert von
idx
zum Zeitpunkt der Kompilierung kennen, damitstd::get<idx>()
er funktioniert, da er als Vorlagenargument verwendet wird.Erste Option: Wenn der Code zur Kompilierungszeit ausgeführt werden soll, machen Sie alles
constexpr
:Dies funktioniert, weil
std::variant
esconstexpr
freundlich ist (seine Konstruktoren und Methoden sind alleconstexpr
).Zweite Möglichkeit: Wenn der Code nicht gemeint ist , bei der Kompilierung laufen, was wahrscheinlich der Fall ist, kann der Compiler bei der Kompilierung nicht ableiten , welche Art von
res
, weil es drei verschiedenen Dinge sein könnte (int
,float
oderchar
). C ++ ist eine statisch typisierte Sprache, und der Compiler muss in der Lage sein, den Typ vonauto res = ...
aus dem folgenden Ausdruck abzuleiten (dh es muss immer der gleiche Typ sein).Sie können
std::get<T>
den Typ anstelle eines Index verwenden, wenn Sie bereits wissen, wie er aussehen wird:std::holds_alternative
Überprüfen Sie im Allgemeinen, ob die Variante jeden der angegebenen Typen enthält, und behandeln Sie sie separat:Alternativ können Sie verwenden
std::visit
. Dies ist etwas komplizierter: Sie können eine Lambda / Templated-Funktion verwenden, die typunabhängig ist und für alle Typen der Variante funktioniert, oder einen Funktor mit einem überlasteten Anrufoperator übergeben:Siehe std :: visit für Details und Beispiele.
quelle
Das Problem ist,
std::get<idx>(var);
dass (füridx
) einen bekannten Wert zur Kompilierungszeit erforderlich ist.Also ein
constexpr
WertAber initialisieren
idx
alsconstexpr
auchvar
sein mussteconstexpr
quelle
Das Problem tritt auf, wenn Vorlagen zur Kompilierungszeit instanziiert werden, während der Index, den Sie erhalten, zur Laufzeit berechnet wird. In ähnlicher Weise werden C ++ - Typen auch zur Kompilierungszeit definiert, sodass sie auch mit der
auto
Deklarationres
einen konkreten Typ haben müssen, damit das Programm gut geformt ist. Dies bedeutet, dass das, was Sie versuchen, auch ohne die Einschränkung der Vorlage für nicht konstante Ausdrücke von Natur aus unmöglich iststd::variant
. Wie würde man das umgehen?Erstens, wenn Ihre Variante tatsächlich ein konstanter Ausdruck ist, wird der Code kompiliert und funktioniert wie erwartet
Andernfalls müssen Sie einen manuellen Verzweigungsmechanismus verwenden
Sie können diese Zweige anhand des Besuchermusters definieren, siehe std :: visit .
quelle
Dies ist im C ++ - Modell von Natur aus unmöglich. Erwägen
Welche
f
heißtf<int>
oderf<double>
? Wenn es "beides" ist, bedeutet dies, dass esg
einen Zweig enthält (was nicht der Fall ist) oder dass es zwei Versionen von gibtg
(was das Problem nur auf den Aufrufer überträgt). Und denken Sie darüber nach -f(T,U,V,W)
wo hört der Compiler auf?Es gibt tatsächlich einen Vorschlag für eine JIT für C ++, die solche Dinge ermöglicht, indem diese zusätzlichen Versionen kompiliert werden,
f
wenn sie aufgerufen werden, aber es ist sehr früh.quelle