J'essaie d'accéder au contenu d'une variante. Je ne sais pas ce qu'il y a dedans, mais heureusement, la variante le fait. J'ai donc pensé demander à la variante sur quel index il se trouve, puis utiliser cet index pour std::get
son contenu.
Mais cela ne compile pas:
#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;
}
L'erreur se produit dans l' std::get
appel:
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();
^~~
Comment puis-je réparer cela?
Réponses:
Essentiellement, vous ne pouvez pas.
Tu as écrit:
... mais uniquement au moment de l'exécution, pas au moment de la compilation.
Et cela signifie que votre
idx
valeur n'est pas au moment de la compilation.Et cela signifie que vous ne pouvez pas utiliser
get<idx>()
directement.Quelque chose que vous pourriez faire est d'avoir une instruction switch; moche, mais ça marcherait:
C'est plutôt moche cependant. Comme le suggèrent les commentaires, vous pourriez aussi bien
std::visit()
(ce qui n'est pas très différent du code ci-dessus, sauf utiliser des arguments de modèles variadiques au lieu d'être aussi explicites) et éviter complètement le commutateur. Pour d'autres approches indexées (non spécifiques àstd::variant
), voir:Idiome pour simuler des paramètres de modèle numérique d'exécution?
la source
Le compilateur doit connaître la valeur de
idx
au moment de la compilation pourstd::get<idx>()
fonctionner, car il est utilisé comme argument de modèle.Première option: si le code est destiné à être exécuté au moment de la compilation, alors faites tout
constexpr
:Cela fonctionne car
std::variant
estconstexpr
convivial (ses constructeurs et méthodes sont tousconstexpr
).Deuxième option: si le code n'est pas destiné à être exécuté au moment de la compilation, ce qui est probablement le cas, le compilateur ne peut pas déduire au moment de la compilation le type de
res
, car il peut s'agir de trois choses différentes (int
,float
ouchar
). C ++ est un langage à typage statique, et le compilateur doit pouvoir déduire le type deauto res = ...
de l'expression qui suit (c'est-à-dire qu'il doit toujours être du même type).Vous pouvez utiliser
std::get<T>
, avec le type au lieu d'un index, si vous savez déjà de quoi il s'agit:En général, utilisez
std::holds_alternative
pour vérifier si la variante contient chacun des types donnés et les gérer séparément:Vous pouvez également utiliser
std::visit
. C'est un peu plus compliqué: vous pouvez utiliser une fonction lambda / basée sur un modèle qui est indépendante du type et fonctionne pour tous les types de la variante, ou passer un foncteur avec un opérateur d'appel surchargé:Voir std :: visit pour les détails et les exemples.
la source
Le problème est qu'il
std::get<idx>(var);
faut (pouridx
) une valeur connue au moment de la compilation.Donc une
constexpr
valeurMais pour initialiser
idx
commeconstexpr
, égalementvar
dû êtreconstexpr
la source
Le problème provient du fait que les modèles sont instanciés au moment de la compilation tandis que l'index que vous obtenez est calculé au moment de l'exécution. De même, les types C ++ sont également définis au moment de la compilation, donc même avec la
auto
déclaration, ilsres
doivent avoir un type concret pour que le programme soit bien formé. Cela signifie que même sans la restriction sur le modèle, ce que vous essayez de faire est intrinsèquement impossible pour les expressions non constantesstd::variant
. Comment contourner cela?Premièrement, si votre variante est en effet une expression constante, le code se compile et fonctionne comme prévu
Sinon, vous devrez utiliser un mécanisme de branchement manuel
Vous pouvez définir ces branches en utilisant le modèle de visiteur, voir std :: visit .
la source
Ceci est intrinsèquement impossible dans le modèle de C ++; considérer
Qui
f
s'appelle,f<int>
ouf<double>
? Si c'est «les deux», cela signifie qu'ilg
contient une branche (ce qui n'est pas le cas), ou qu'il existe deux versions deg
(qui ne font que pousser le problème sur son appelant). Et pensez à:f(T,U,V,W)
où s'arrête le compilateur?Il y a en fait une proposition de JIT pour C ++ qui permettrait des choses comme ça en compilant ces versions supplémentaires de
f
quand elles sont appelées, mais c'est très tôt.la source