Estou tentando acessar o conteúdo de uma variante. Não sei o que há lá, mas, felizmente, a variante sabe. Então, pensei em perguntar à variante em que índice ela está e depois usá-la em std::get
seu conteúdo.
Mas isso não compila:
#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;
}
O erro ocorre na std::get
chamada:
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();
^~~
Como posso consertar isso?
Respostas:
Essencialmente, você não pode.
Você escreveu:
... mas apenas no tempo de execução, não no tempo de compilação.
E isso significa que seu
idx
valor não é em tempo de compilação.E isso significa que você não pode usar
get<idx>()
diretamente.Algo que você pode fazer é ter uma instrução switch; feio, mas funcionaria:
Isso é bastante feio, no entanto. Como os comentários sugerem, você também pode
std::visit()
(que não é muito diferente do código acima, exceto usar argumentos de modelo variados em vez de ser explícito) e evitar a mudança completamente. Para outras abordagens baseadas em índice (não específicas parastd::variant
), consulte:Idioma para simular parâmetros numéricos de modelo em tempo de execução?
fonte
O compilador precisa saber o valor de
idx
no momento da compilação parastd::get<idx>()
funcionar, porque está sendo usado como argumento de modelo.Primeira opção: se o código for executado em tempo de compilação, faça tudo
constexpr
:Isso funciona porque
std::variant
éconstexpr
amigável (seus construtores e métodos são todosconstexpr
).Segunda opção: se o código não for executado em tempo de compilação, o que provavelmente é o caso, o compilador não poderá deduzir em tempo de compilação o tipo de
res
, porque pode haver três coisas diferentes (int
,float
ouchar
). C ++ é uma linguagem de tipo estaticamente, e o compilador deve poder deduzir o tipo deauto res = ...
da expressão a seguir (ou seja, sempre deve ser o mesmo tipo).Você pode usar
std::get<T>
, com o tipo em vez de um índice, se você já sabe o que será:Em geral, use
std::holds_alternative
para verificar se a variante está mantendo cada um dos tipos fornecidos e manipule-os separadamente:Alternativamente, você pode usar
std::visit
. Isso é um pouco mais complicado: você pode usar uma função lambda / modelo que é independente de tipo e funciona para todos os tipos de variantes, ou passa um functor com um operador de chamada sobrecarregado:Veja std :: visit para detalhes e exemplos.
fonte
O problema é que
std::get<idx>(var);
requer (poridx
) um valor conhecido em tempo de compilação.Então um
constexpr
valorMas para inicializar
idx
comoconstexpr
, tambémvar
tinha que serconstexpr
fonte
O problema surge quando os modelos são instanciados em tempo de compilação, enquanto o índice que você está obtendo é calculado em tempo de execução. Da mesma forma, os tipos C ++ também são definidos em tempo de compilação, portanto, mesmo com a
auto
declaração,res
deve haver um tipo concreto para que o programa seja bem formado. Isso significa que, mesmo sem a restrição no modelo, o que você está tentando fazer é inerentemente impossível para expressões não constantesstd::variant
s. Como se resolveria isso?Em primeiro lugar, se sua variante é realmente uma expressão constante, o código compila e funciona conforme o esperado
Caso contrário, você terá que usar algum mecanismo de ramificação manual
Você pode definir essas ramificações usando o padrão de visitante, consulte std :: visit .
fonte
Isso é inerentemente impossível no modelo de C ++; considerar
Qual
f
está sendo chamadof<int>
ouf<double>
? Se for "ambos", significa queg
contém um ramo (o que não existe) ou que existem duas versõesg
(o que apenas coloca o problema no chamador). E pense:f(T,U,V,W)
onde o compilador para?Na verdade, existe uma proposta para um JIT para C ++ que permitiria coisas assim, compilando essas versões adicionais de
f
quando elas são chamadas, mas é muito cedo.fonte