У меня есть целочисленный массив для извлечения содержимого в 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]> используется для доступа к элементу во входном векторе. Можете ли вы предложить четкую и элегантную выкройку, подходящую для моего случая?
Большое спасибо!
Что должно означать constexpr int[] expected_types = {2, 2, 2, 2};
?
Здесь есть несколько проблем. Вариант имеет три возможных значения. Цикл повторяется четыре раза. Это имеет для вас смысл? Вариант имеет одно из нескольких возможных значений по определению. Вот что такое вариант, у этого есть либо int
, либо float
, либо bool
. Только один из них. Так что же делает проверка варианта три или четыре раза в разное время? Наконец, параметры шаблона должны быть указаны во время компиляции, а не во время выполнения. Как сказал бы мистер Спок: все это нелогично. Прежде чем получить это задание по программированию, вам должны были объяснить эти концепции. Что было непонятно?
get()
— это шаблонная функция. Вы не можете передавать переменные среды выполнения в аргументы шаблона. Так что этот подход никогда не сработает
@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, почему его все еще нельзя скомпилировать?
@RemyLebeau Привет, Реми, спасибо, что указали на ошибку. Но ожидаемые_типы — это constexpr, так что это известно во время компиляции. Можете ли вы предложить решение по этому поводу?
@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]> используется для доступа к элементу во входном векторе. Можете ли вы предложить четкую реализацию для достижения этого на С++?
@Bear Тогда я полагаю, вы имеете в виду constexpr int expected_types[] = {2,2,2,2};
и std::get<expected_types[i]>(input[i])
, верно? Не то, чтобы это тоже сработало, но это все же более правильно :-) Кроме того, если вы хотите проверить, что vector
из variant
содержит ожидаемые типы, должны ли вы действительно использовать логическое OR
вместо логического AND
?
Что вы подразумеваете под «Затем std::get<expected_types[i]> используется для доступа к элементу во входном векторе»? Вы пытаетесь проверить, что vector
из variant
соответствуют правильным типам, чтобы вы хотели каким-то образом получить доступ к значениям?
Наконец-то я понял, о чем здесь спрашивали. Ни один из ответов здесь не касался этого напрямую - удобный способ определить типы для извлечения из каждого из значений. Недостаток ясности усугублялся поддельным кодом с щедрым разбрызгиванием «мое-это» и «мое-то», которые затуманивали реальный вопрос, а не настоящий код. Обычно это делается с помощью комбинации std::index_sequence
, специализации и некоторых вспомогательных шаблонов. Это всегда грязно и запутанно, но занимает много слов. Но это выполнимо.
@Biear «expected_types
— это constexpr
, так что это известно во время компиляции», а i
— нет. Массив может быть constexpr
, но вы перебираете его во время выполнения. Шаблоны могут быть созданы только во время компиляции, поэтому std::get<expected_types[i]>
не может работать, когда i
является счетчиком циклов во время выполнения.
@Biear Если ни один из ответов не отвечает на ваш вопрос, вам, вероятно, нужно обновить вопрос, чтобы предоставить подробности и ясность. Если вы объедините его с некоторыми примерами и результатом, который вы ожидаете от своей функции, будет намного проще сделать ответ, который предоставит вам необходимую информацию.
@Sam Varshavchik Привет, Сэм, не могли бы вы поделиться фрагментом кода этого вопроса? Спасибо!
@SamVarshavchik Re: «Ни один из ответов здесь не касался этого напрямую» - к чему относится «это»? На первый взгляд, ваш ответ на С++ 17 очень похож на мой ответ на С++ 20, поэтому мне любопытно, что я пропустил.
Это касалось двух ответов, которые были опубликованы ранее, @TedLyngmo. Я оставил вопрос и вернулся к нему после того, как, наконец, понял, что пытался спросить ОП, что пропустили предыдущие ответы.
@SamVarshavchik Ага, понятно. Спасибо, что прояснили это :-)
Поскольку функция 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.
вы можете получить доступ к варианту, только указав тип или индекс, например:
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>>
...
A
, B
или C
.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::get
expected_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 Бесплатные шаблоны функций в моем ответе могут использоваться A, B и C для реализации интерфейса. Вы также можете поместить реализацию интерфейса в базу CRTP или использовать полиморфизм времени выполнения. Не могли бы вы обновить вопрос, чтобы четко показать, как должен выглядеть интерфейс и что вы ожидаете от него, если неясно, как следует использовать два шаблона функций, которые я сделал?
Привет @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
@Bear Добро пожаловать! Если вы только что перешли с Java на C++, я приятно удивлен, что это заняло всего несколько дней. С++ иногда может выглядеть ужасно :-) Что касается вашего нового вопроса: лучше принять любой из ответов на этот старый вопрос (если вы нашли тот, который ответил на ваш вопрос), а затем задать новый вопрос. Желательно, чтобы каждый вопрос касался одной конкретной вещи.
Спасибо за ваш совет! Я закрыл этот и открыл новый вопрос. Не могли бы вы взглянуть на новый stackoverflow.com/questions/75370505?
Как я уже упоминал, такая валидация доставляет массу неудобств и требует многословия, включая специализацию.
Здесь используется синтаксис 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;
}
Вы имели в виду
std::get<expected_types[i]>
?