Найти элемент в std::vector из std::any
Я хочу проверить, существует ли элемент в векторе или нет. Я знаю, что следующий фрагмент кода проверит это.
#include <algorithm>
if ( std::find(vector.begin(), vector.end(), item) != vector.end() )
std::cout << "found";
else
std::cout << "not found";
Но у меня есть вектор любого типа. т.е. std::vector<std::any>
Я вставляю элементы в вектор вот так.
std::vector<std::any> temp;
temp.emplace_back(std::string("A"));
temp.emplace_back(10);
temp.emplace_back(3.14f);
Поэтому мне нужно найти, присутствует ли строка "А" в векторе или нет. Может ли std:: найти помощь здесь?
На данный момент я использую приведенный ниже кусок кода, чтобы сделать это
bool isItemPresentInAnyVector(std::vector<std::any> items, std::any item)
{
for (const auto& it : items)
{
if (it.type() == typeid(std::string) && item.type() == typeid(std::string))
{
std::string strVecItem = std::any_cast<std::string>(it);
std::string strItem = std::any_cast<std::string>(item);
if (strVecItem.compare(strItem) == 0)
return true;
}
else if (it.type() == typeid(int) && item.type() == typeid(int))
{
int iVecItem = std::any_cast<int>(it);
int iItem = std::any_cast<int>(item);
if (iVecItem == iItem)
return true;
}
else if (it.type() == typeid(float) && item.type() == typeid(float))
{
float fVecItem = std::any_cast<float>(it);
float fItem = std::any_cast<float>(item);
if (fVecItem == fItem)
return true;
}
}
return false;
}
5 ответов
Это должно работать хорошо, я думаю:
#include <vector>
#include <string>
#include <any>
#include <algorithm>
#include <iostream>
int main(){
std::vector<std::any> temp;
temp.emplace_back(std::string("A"));
temp.emplace_back(10);
temp.emplace_back(3.14f);
int i = 10;//you can use any type for i variable and it should work fine
//std::string i = "A";
auto found = std::find_if(temp.begin(), temp.end(), [i](const auto &a){
return typeid(i) == a.type() && std::any_cast<decltype(i)>(a) == i;
} );
std::cout << std::any_cast<decltype(i)>(*found);
}
Или сделать код немного более универсальным и пригодным для повторного использования:
#include <vector>
#include <string>
#include <any>
#include <algorithm>
#include <iostream>
auto any_compare = [](const auto &i){
return [i] (const auto &val){
return typeid(i) == val.type() && std::any_cast<decltype(i)>(val) == i;
};
};
int main(){
std::vector<std::any> temp;
temp.emplace_back(std::string("A"));
temp.emplace_back(10);
temp.emplace_back(3.14f);
//int i = 10;
std::string i = "A";
auto found = std::find_if(temp.begin(), temp.end(), any_compare(i));
std::cout << std::any_cast<decltype(i)>(*found);
}
Важное примечание: это гарантированно работает только в пределах одной единицы перевода из-за стандартных требований на std::any
тип (например, одни и те же типы не должны иметь одинаковый идентификатор типа в разных единицах перевода)
Используя any
для такого рода целей не является хорошим использованием any
, Лучший способ - просто использовать variant
- поскольку у вас есть закрытый набор типов:
struct Equals {
template <typename T>
constexpr bool operator()(T const& a, T const& b) const { return a == b; }
template <typename T, typename U>
constexpr bool operator()(T const& a, U const& b) const { return false; }
};
using V = std::variant<int, float, std::string>
bool isItemPresentInAnyVector(std::vector<V> const& items, V const& item)
{
auto it = std::find_if(items.begin(), items.end(), [&](V const& elem){
return std::visit(Equals{}, elem, item);
});
return it != items.end();
}
На самом деле это даже лучше, потому что, как указывает Килиан, variant
"s operator==
уже работает именно так:
using V = std::variant<int, float, std::string>
bool isItemPresentInAnyVector(std::vector<V> const& items, V const& item)
{
return std::find(items.begin(), items.end(), item) != items.end();
}
К сожалению, если вы хотите найти std::any
экземпляр в векторе std::any
В случаях, если ответ отрицательный.
std::any
Например, требуется некоторая "магия", чтобы иметь возможность обрабатывать создание неизвестных типов объектов, но этот механизм является частным и должен поддерживать только создание объектов, а не сравнение на равенство.
Было бы возможно реализовать то, что вы ищете, используя тот же подход, но не со стандартным std::any
это не публикует необходимые детали. Шаблон "manager" должен перечислять все возможные операции, и, например, в реализации g++ это "access", "get_type_info", "clone", "destroy", "xfer".
variant
полностью отличается, потому что явно перечисляет все разрешенные типы и, следовательно, в любом месте, где он используется, может получить доступ ко всем методам.
В сравнении с
typeId()
следует избегать, так как это зависит от единицы перевода.
Гораздо более безопасный подход может быть использован с
any_cast
указателей:
template<typename T>
std::optional<T> find(const std::vector<std::any>& v)
{
for(auto&& e : v){
if(auto ptr = std::any_cast<T>(&e)){
return *ptr;
}
}
return std::nullopt;
}
Найдите первый элемент с заданным типом или
nullopt
если он не найден.
Если мы хотим найти все элементы с конкретным вместо этого:
template<typename T>
std::vector<T> findAll(const std::vector<std::any>& v)
{
std::vector<T> out;
for(auto&& e : v){
if(auto ptr = std::any_cast<T>(&e)){
out.push_back(*ptr);
}
}
return out;
}
Применение:
int main()
{
std::vector<std::any> temp;
temp.emplace_back(std::string("A"));
temp.emplace_back(10);
temp.emplace_back(3.14f);
temp.emplace_back(12);
temp.emplace_back(std::string("B"));
auto outInt = findAll<int>(temp);
std::cout << "out int: " << outInt.size() << std::endl;
for(auto&& out : outInt)
std::cout << out << std::endl;
auto outString = findAll<std::string>(temp);
std::cout << "out string: " << outString.size() << std::endl;
for(auto&& out : outString)
std::cout << out << std::endl;
auto singleInt = find<int>(temp);
if(singleInt)
std::cout << "first int " << *singleInt << std::endl;
auto singleBool = find<bool>(temp);
if(!singleBool)
std::cout << "ok: bool not found" << std::endl;
}
Если типы int
, float
а также string
(или ограниченный набор типов), вы можете использовать комбинацию std::variant
а также std::get_if
добиться того, что вы хотите сделать, простым способом:
std::get_if
чтобы определить, какой из типов хранится в std::variant
,
Минимальный пример:
#include <iostream>
#include <vector>
#include <string>
#include <variant>
int main(){
std::vector<std::variant<int, float, std::string>> temp;
temp.emplace_back(std::string("A"));
temp.emplace_back(10);
temp.emplace_back(3.14f);
for (const auto& var: temp) {
if(std::get_if<std::string>(&var)) {
if(std::get<std::string>(var) == "A") std::cout << "found string\n";
}
if(std::get_if<int>(&var)) {
if(std::get<int>(var) == 10) std::cout << "found int\n";
}
if(std::get_if<float>(&var)) {
if(std::get<float>(var) == 3.14f) std::cout << "found float\n";
}
}
}