Я пытаюсь получить доступ к содержанию варианта. Я не знаю, что там, но, к счастью, вариант делает. Поэтому я подумал, что просто спрошу у варианта, в каком индексе он находится, а затем использую этот индекс для std::get
своего содержимого.
Но это не компилируется:
#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;
}
Ошибка происходит при std::get
звонке:
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();
^~~
Как я могу это исправить?
Ответы:
По сути, вы не можете.
Вы написали:
... но только во время выполнения, а не во время компиляции.
А это значит, что ваша
idx
ценность не во время компиляции.А это значит, что вы не можете использовать
get<idx>()
напрямую.Что-то, что вы могли бы сделать, это иметь оператор switch; некрасиво, но сработало бы
Это довольно некрасиво, однако. Как показывают комментарии, вы также можете
std::visit()
(что не сильно отличается от приведенного выше кода, за исключением использования аргументов шаблона переменной вместо того, чтобы быть явным) и вообще избежать переключения. Другие подходы, основанные на индексах (не специфичные дляstd::variant
), см.Идиома для моделирования параметров числового шаблона во время выполнения?
источник
Для работы компилятору необходимо знать значение
idx
во время компиляцииstd::get<idx>()
, поскольку оно используется в качестве аргумента шаблона.Первый вариант: если код предназначен для запуска во время компиляции, то сделайте все
constexpr
:Это работает , потому что
std::variant
этоconstexpr
дружеский (его конструкторы и методы всеconstexpr
).Второй вариант: Если код не предназначен для работы во время компиляции, что, скорее всего , в случае, компилятор не может вывести во время компиляции типа
res
, потому что это может быть три разные вещи (int
,float
илиchar
). C ++ является статически типизированным языком, и компилятор должен иметь возможность определить типauto res = ...
выражения из следующего выражения (то есть он всегда должен быть одного типа).Вы можете использовать
std::get<T>
с типом вместо индекса, если вы уже знаете, что это будет:В общем, используйте
std::holds_alternative
для проверки, содержит ли вариант каждый из указанных типов, и обрабатывайте их отдельно:В качестве альтернативы вы можете использовать
std::visit
. Это немного сложнее: вы можете использовать лямбда / шаблонную функцию, которая не зависит от типа и работает для всех типов варианта, или передать функтор с перегруженным оператором вызова:Смотрите std :: visit для подробностей и примеров.
источник
Проблема в том, что
std::get<idx>(var);
требуется (дляidx
) известное значение времени компиляции.Так что
constexpr
значениеНо для инициализации ,
idx
какconstexpr
, также должныvar
были бытьconstexpr
источник
Проблема возникает из-за того, что шаблоны создаются во время компиляции, а индекс, который вы получаете, вычисляется во время выполнения. Точно так же типы C ++ также определяются во время компиляции, поэтому даже с
auto
объявлениемres
должен быть конкретный тип, чтобы программа была правильно сформирована. Это означает, что даже без ограничения на шаблон то, что вы пытаетесь сделать, невозможно для непостоянных выраженийstd::variant
s. Как можно обойти это?Во-первых, если ваш вариант действительно является константным выражением, код компилируется и работает как положено
В противном случае вам придется использовать какой-то ручной механизм ветвления.
Вы можете определить эти ветви, используя шаблон посетителя, см. Std :: visit .
источник
Это по своей сути невозможно в модели C ++; рассматривать
Который
f
называется,f<int>
илиf<double>
? Если это «оба», это означает, что в немg
содержится ветвь (которой нет), или что существует две версииg
(которая просто выдвигает проблему на вызывающую сторону). И подумайте:f(T,U,V,W)
где остановится компилятор?На самом деле есть предложение для JIT для C ++, которое позволит делать такие вещи, компилируя эти дополнительные версии,
f
когда они вызываются, но это очень рано.источник