Std::получить содержимое из std::variant, используя элемент из целочисленного массива в качестве целевого типа в std::variant

У меня есть целочисленный массив для извлечения содержимого в std::variant. Но компиляция не удалась, сообщение об ошибке No matching function to call 'get'. Можете ли вы объяснить, почему, и предложить рабочее решение для достижения той же цели?

using my_type = std::variant<int, float, bool>;
constexpr int[] expected_types = {2,2,2,2};
 

bool validate(std::vector<my_type> input) {
  bool rst;
  if (input.size() != 4) {
    return false;
  }
  for (int i = 0; i < 4; i++) {
    rst = rst || std::get<my_type[i]>(input[i]);
  }
  return rst;
}

Вот еще контекст. Классы A, B, C реализуют интерфейс bool foo(std::vector<my_type> input). Но входные данные класса A имеют формат {int, int, bool}, входные данные класса B — в формате {int, int, float}, входные данные класса C — в формате {bool, bool}. ожидаемые_типы в каждом классе сохраняют свои типы во входном векторе. Затем std::get<expected_types[i]> используется для доступа к элементу во входном векторе. Можете ли вы предложить четкую и элегантную выкройку, подходящую для моего случая?

Большое спасибо!

Вы имели в виду std::get<expected_types[i]>?

Alan Birtles 30.01.2023 08:01

Что должно означать constexpr int[] expected_types = {2, 2, 2, 2};?

Ted Lyngmo 30.01.2023 08:07

Здесь есть несколько проблем. Вариант имеет три возможных значения. Цикл повторяется четыре раза. Это имеет для вас смысл? Вариант имеет одно из нескольких возможных значений по определению. Вот что такое вариант, у этого есть либо int, либо float, либо bool. Только один из них. Так что же делает проверка варианта три или четыре раза в разное время? Наконец, параметры шаблона должны быть указаны во время компиляции, а не во время выполнения. Как сказал бы мистер Спок: все это нелогично. Прежде чем получить это задание по программированию, вам должны были объяснить эти концепции. Что было непонятно?

Sam Varshavchik 30.01.2023 08:48
get() — это шаблонная функция. Вы не можете передавать переменные среды выполнения в аргументы шаблона. Так что этот подход никогда не сработает
Remy Lebeau 30.01.2023 08:48

@TedLyngmo Lyngmo Идея в том, что класс A и класс B реализуют интерфейс bool foo(std::vector<my_type> input). Но входные данные класса A имеют формат {int, int, bool}, а входные данные класса B — в формате {int, int, float}. std::get<expected_types[i]>(input) помогает мне получить соответствующие значения в классе A или классе B. Моя путаница/вопрос заключается в том, что std::get<expected_types[i]>(input) вызовет ошибку компиляции, но std::get<expected_types[0]>(input) не будет. Посколькуожидаемые_типы уже являются constexpr, почему его все еще нельзя скомпилировать?

Biear 31.01.2023 08:24

@RemyLebeau Привет, Реми, спасибо, что указали на ошибку. Но ожидаемые_типы — это constexpr, так что это известно во время компиляции. Можете ли вы предложить решение по этому поводу?

Biear 31.01.2023 08:30

@SamVarshavchik Привет, Сэм, вот еще контекст. Классы A, B, C реализуют интерфейс bool foo(std::vector<my_type> input). Но входные данные класса A имеют формат {int, int, bool}, входные данные класса B — в формате {int, int, float}, входные данные класса C — в формате {bool, bool}. ожидаемые_типы в каждом классе сохраняют свои типы во входном векторе. Затем std::get<expected_types[i]> используется для доступа к элементу во входном векторе. Можете ли вы предложить четкую реализацию для достижения этого на С++?

Biear 31.01.2023 08:37

@Bear Тогда я полагаю, вы имеете в виду constexpr int expected_types[] = {2,2,2,2}; и std::get<expected_types[i]>(input[i]), верно? Не то, чтобы это тоже сработало, но это все же более правильно :-) Кроме того, если вы хотите проверить, что vector из variant содержит ожидаемые типы, должны ли вы действительно использовать логическое OR вместо логического AND?

Ted Lyngmo 31.01.2023 09:29

Что вы подразумеваете под «Затем std::get<expected_types[i]> используется для доступа к элементу во входном векторе»? Вы пытаетесь проверить, что vector из variant соответствуют правильным типам, чтобы вы хотели каким-то образом получить доступ к значениям?

Ted Lyngmo 31.01.2023 09:45

Наконец-то я понял, о чем здесь спрашивали. Ни один из ответов здесь не касался этого напрямую - удобный способ определить типы для извлечения из каждого из значений. Недостаток ясности усугублялся поддельным кодом с щедрым разбрызгиванием «мое-это» и «мое-то», которые затуманивали реальный вопрос, а не настоящий код. Обычно это делается с помощью комбинации std::index_sequence, специализации и некоторых вспомогательных шаблонов. Это всегда грязно и запутанно, но занимает много слов. Но это выполнимо.

Sam Varshavchik 31.01.2023 15:56

@Biear «expected_types — это constexpr, так что это известно во время компиляции», а i — нет. Массив может быть constexpr, но вы перебираете его во время выполнения. Шаблоны могут быть созданы только во время компиляции, поэтому std::get<expected_types[i]> не может работать, когда i является счетчиком циклов во время выполнения.

Remy Lebeau 31.01.2023 16:35

@Biear Если ни один из ответов не отвечает на ваш вопрос, вам, вероятно, нужно обновить вопрос, чтобы предоставить подробности и ясность. Если вы объедините его с некоторыми примерами и результатом, который вы ожидаете от своей функции, будет намного проще сделать ответ, который предоставит вам необходимую информацию.

Ted Lyngmo 31.01.2023 16:53

@Sam Varshavchik Привет, Сэм, не могли бы вы поделиться фрагментом кода этого вопроса? Спасибо!

Biear 03.02.2023 07:58

@SamVarshavchik Re: «Ни один из ответов здесь не касался этого напрямую» - к чему относится «это»? На первый взгляд, ваш ответ на С++ 17 очень похож на мой ответ на С++ 20, поэтому мне любопытно, что я пропустил.

Ted Lyngmo 07.02.2023 08:41

Это касалось двух ответов, которые были опубликованы ранее, @TedLyngmo. Я оставил вопрос и вернулся к нему после того, как, наконец, понял, что пытался спросить ОП, что пропустили предыдущие ответы.

Sam Varshavchik 07.02.2023 13:04

@SamVarshavchik Ага, понятно. Спасибо, что прояснили это :-)

Ted Lyngmo 07.02.2023 13:04
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
16
152
6
Перейти к ответу Данный вопрос помечен как решенный

Ответы 6

Поскольку функция get ожидает либо тип, либо константу времени компиляции в качестве индекса, ваш подход не сработает.

Чтобы достичь того, чего вы хотите, вам нужно изменить свою функцию, чтобы сделать ее шаблоном функции.

Затем при вызове вашей функции «валидации» вы можете передать значение индекса логического значения в качестве константного параметра шаблона времени компиляции.

Тогда ваша программа будет выглядеть так:

#include <iostream>
#include <iomanip>
#include <variant>
#include <vector>

using my_type = std::variant<int, float, bool>;
constexpr size_t intPart = 0;
constexpr size_t floatPart = 1;
constexpr size_t boolPart = 2;

template <std::size_t I>
bool validate(std::vector<my_type> input) {
    bool rst{};
    if (input.size() != 4) {
        return false;
    }
    for (int i = 0; i < 4; i++) {
        rst = rst || std::get<I>(input[i]);
    }
    return rst;
}
int main() {
    std::vector<my_type> boolData(4);
    boolData[0].emplace<boolPart>(false);
    boolData[1].emplace<boolPart>(false);
    boolData[2].emplace<boolPart>(true);
    boolData[3].emplace<boolPart>(false);

    bool result = validate<boolPart>(boolData);

    std::cout << "Result = " << std::boolalpha << result << '\n';
}

Если позже вам понадобится расширить функцию проверки для обработки других типов ваших std::variant, вы можете сделать это с помощью вариативных шаблонов и constexpr if

Пожалуйста, оставьте отзыв, если это решение достаточно для вас.

my_type не является массивом, поэтому my_type[i] не имеет смысла.

Я предполагаю, что std::get<my_type[i]>(input[i]) должно было быть std::get<expected_types[i]>(input[i])? Это тоже не сработает, поскольку i не является константой времени компиляции, поэтому его нельзя использовать в качестве шаблонного выражения.

Одним из обходных путей было бы развернуть цикл for:

#include <vector>
#include <variant>
#include <array>

using my_type = std::variant<int, float, bool>;
constexpr std::array<int,4> expected_types = {2,2,2,2};
 

bool validate(std::vector<my_type> input) {
  bool rst = false;
  if (input.size() != 4) {
    return false;
  }
  rst = rst || std::get<expected_types[0]>(input[0]);
  rst = rst || std::get<expected_types[1]>(input[1]);
  rst = rst || std::get<expected_types[2]>(input[2]);
  rst = rst || std::get<expected_types[3]>(input[3]);
  return rst;
}

int main()
{
    unsigned long long i = reinterpret_cast<unsigned long long>(nullptr)    ;
}

Если вы на самом деле пытаетесь проверить, соответствуют ли типы ожидаемым, вы можете просто использовать метод index:

bool validate(std::vector<my_type> input) {
  bool rst = false;
  if (input.size() != 4) {
    return false;
  }
  for (std::size_t i=0; i < 4; i++)
  {
    rst = rst || expected_types[i] == input[i].index();
  }
  return rst;
}

Привет, Алан, > Вы имели в виду std::get<expected_types[i]> Да, я хочу использовать std::get<expected_types[i]> для получения типизированных значений класса. Например, classA имеет ожидаемые_типы{1,1,2}, classB имеет ожидаемые_типы{0,0,1}. Я хочу, чтобы std::get<expected_types[0]> получал число с плавающей запятой в классе A и int в классе B.

Biear 31.01.2023 08:11

вы можете получить доступ к варианту, только указав тип или индекс, например:

std::get<int>(variant)
std::get<0>(variant)

Но ваша проблема с доступом к варианту из вектора в цикле без явного указания типа или индекса может быть решена с помощью посетителя (нам даже не нужна другая переменная, чтобы сохранить индекс для типа варианта в векторе)

Структура посетителя может использоваться для выполнения логической или операции над значением варианта и сохранения результата, который может быть возвращен позже.

#include <variant>
#include <iostream>
#include <vector>

using my_type = std::variant<int, float, bool>;
//constexpr int[] expected_types = {2,2,2,2};

struct logicalOrOperationVisitor
{
    bool rst;
    
    logicalOrOperationVisitor(bool b) : rst(b) { }
    
    void operator()(int &i)
    {
        rst = rst || i;
    }
    
    void operator()(float &f)
    {
        rst = rst || f;
    }
    
    void operator()(bool &b)
    {
        rst = rst || b;
    }
};


bool validate(std::vector<my_type> input) {
  logicalOrOperationVisitor orRst(false);
  
  if (input.size() != 4) {
    return false;
  }
  for (int i = 0; i < 4; i++) {
    //rst = rst || std::get<my_type[i]>(input[i]);
    std::visit(orRst, input[i]);
  }
  return orRst.rst;
}
Ответ принят как подходящий

Если я правильно понимаю вашу функцию validate, она должна подтвердить, что vector<variant<int, float, bool>>...

  1. Имеет правильную длину для типа A, B или C.
  2. Содержит типы variant, обозначенные индексами variant в A::expected_types[], B::expected_types[] или C::expected_types[].

Затем он может взять тип, для которого тестируется vector (A, B или C), в качестве параметра шаблона и использовать variant::index(), чтобы проверить, какой тип содержится в variant отдельных vector.

Объедините это со сгибом && (так как вы хотите, чтобы все было true), и вы получите:

template<class T>
bool validate(const std::vector<my_type>& input) {
    if (input.size() != std::size(T::expected_types))  return false;

    return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
        // fold over && :
        return (... && (input[Is].index() == T::expected_types[Is]));
    }(std::make_index_sequence<std::size(T::expected_types)>());
}

Если вы действительно хотите std::getexpected_types из vector и использовать логические OR, это можно сделать аналогичным образом, но сворачивая ||:

template <class T>
bool get_bool(const std::vector<my_type>& input) {
    if (not validate<T>(input)) return false; // use the validation function above

    return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
        // fold over ||
        return (... || (std::get<T::expected_types[Is]>(input[Is])));
    }(std::make_index_sequence<std::size(T::expected_types)>());
}

Демо

Спасибо, Тед! Ваше решение аккуратное, мне нужно изучить это решение! В моем случае классы A, B, C реализуют интерфейс bool foo(std::vector<my_type> input). Но входные данные класса A имеют формат {int, int, bool}, входные данные класса B — в формате {int, int, float}, входные данные класса C — в формате {bool, bool}. У вас есть идея получше, чтобы решить это элегантно? Есть ли лучшее решение, чем использование std::vector<std::variant<int, float, bool>> во входном аргументе?

Biear 01.02.2023 09:08

@Biear Бесплатные шаблоны функций в моем ответе могут использоваться A, B и C для реализации интерфейса. Вы также можете поместить реализацию интерфейса в базу CRTP или использовать полиморфизм времени выполнения. Не могли бы вы обновить вопрос, чтобы четко показать, как должен выглядеть интерфейс и что вы ожидаете от него, если неясно, как следует использовать два шаблона функций, которые я сделал?

Ted Lyngmo 01.02.2023 09:55

Привет @Ted Lyngmo, спасибо за ответ на мой вопрос. Мне нужно несколько дней, чтобы понять ваш код. Я только что переключился с Java-разработчика на С++. Отсюда более общий вопрос. У меня есть еще одна функция void record(vector<my_type> input) для реализации. Он принимает входной вектор и делает что-то, используя содержимое этого вектора. ``` class A { constexpr int[] expect_types = {2,0,1,2}; недействительная запись (вектор <my_type> input) { std::get<expected_types[0]>(input[0]); // Есть ли более формальный способ получить содержимое? } } ``` Выполняемый фрагмент кода shorturl.at/erY17

Biear 07.02.2023 08:52

@Bear Добро пожаловать! Если вы только что перешли с Java на C++, я приятно удивлен, что это заняло всего несколько дней. С++ иногда может выглядеть ужасно :-) Что касается вашего нового вопроса: лучше принять любой из ответов на этот старый вопрос (если вы нашли тот, который ответил на ваш вопрос), а затем задать новый вопрос. Желательно, чтобы каждый вопрос касался одной конкретной вещи.

Ted Lyngmo 07.02.2023 08:52

Спасибо за ваш совет! Я закрыл этот и открыл новый вопрос. Не могли бы вы взглянуть на новый stackoverflow.com/questions/75370505?

Biear 07.02.2023 09:12

Как я уже упоминал, такая валидация доставляет массу неудобств и требует многословия, включая специализацию.

Здесь используется синтаксис C++17, что, как мне кажется, разумно в 2023 году. Это также подтверждает, что каждый вариант в векторе на самом деле является правильным типом, и в противном случае не проходит проверку. Это не было явно указано в вопросе, но я вполне уверен, что это подразумевалось.

#include <utility>
#include <variant>
#include <cstdlib>
#include <vector>
#include <iostream>

using my_type = std::variant<int, float, bool>;
constexpr size_t expected_types[] = {0, 2, 2, 2};

template<typename T>
struct do_validate;

template<size_t ...N>
struct do_validate<std::index_sequence<N...>> {

    static bool validate(const std::vector<my_type> &input)
    {
        if (input.size() != sizeof...(N))
            return false;

        return ( (expected_types[N] ==
              input[N].index() &&
              std::get<expected_types[N]>(input[N])) || ...);
    }
};

bool validate(const std::vector<my_type> &input)
{
    return do_validate<std::make_index_sequence<std::size(expected_types)>>
        ::validate(input);
}

int main()
{
    std::vector<my_type> n{{
            my_type{ std::in_place_index<0>, 1},
            my_type{ std::in_place_index<2>, false},
            my_type{ std::in_place_index<2>, false},
            my_type{ std::in_place_index<2>, false}
        }};

    std::cout << validate(n) << "\n";
    return 0;
}

Использование std::variant::index позволяет достичь цели красивым способом:

template<typename T>
bool validate(std::vector<my_type> input, const T& expectedTypes)
{
    if (input.size() != std::size(expectedTypes)) {
        return false;
    }
    bool rst = true;
    auto it = std::begin(input);
    for (auto expectedType : expectedTypes) {
        rst = rst && (it->index() == expectedType);
        ++it;
    }
    return rst;
}

https://godbolt.org/z/G9aMfWfEn

Другие вопросы по теме