【问题标题】:find an element in std::vector of std::any在 std::any 的 std::vector 中找到一个元素
【发布时间】:2019-07-30 04:11:27
【问题描述】:

我想检查一个元素是否存在于向量中。我知道下面的代码会检查它。

#include <algorithm>

if ( std::find(vector.begin(), vector.end(), item) != vector.end() )
   std::cout << "found";
else
   std::cout << "not found";

但我有任何类型的向量。即std::vector&lt;std::any&gt; 我正在像这样将元素推入向量中。

std::vector<std::any> temp;
temp.emplace_back(std::string("A"));
temp.emplace_back(10);
temp.emplace_back(3.14f);

所以我需要找出向量中是否存在字符串“A”。 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;
}

【问题讨论】:

  • 阅读std::find_if
  • 您是否考虑过改用std::variant&lt;int, float, std::string&gt;std::find 可以正常工作。
  • std::any 的通用比较需要 any 本身的支持(因为您不能基于 type() 进行 any_cast,这在编译时是未知的);对于您似乎正在做的事情,variant 在多个维度上确实更好 - 没有额外的堆开销,没有隐藏的虚拟调度等......

标签: c++ c++17 stdany


【解决方案1】:

应避免与typeId() 比较,因为它依赖于翻译单元。

any_cast of 指针可以使用更安全的方法:

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;
}

LIVE DEMO

【讨论】:

    【解决方案2】:

    我猜这应该很好用:

    #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);
    }
    

    Live demo

    重要提示:由于对std::any 类型的标准要求,这保证仅在单个翻译单元中有效(例如,相同的类型不需要在不同的翻译单元中具有相同的类型标识符)

    【讨论】:

    • 注意std::find_if(temp.begin(), temp.end(), any_compare(temp[0])) 会失败(any of any)。
    • @Jarod42 为简单起见,我省略了它,但我可以修复它
    • @bartop:我想说在any 的向量中找到any 在设计上是不可能的(可移植的)。它可以以不同的方式实现std::any,但我不明白如果处理虚拟调度的“管理器”没有,那么您最终如何在从未见过any 对象中包装的类型的编译单元中调用相等比较' 不明确支持该操作(而 g++ 不支持,因为标准不需要它)。
    • @6502 你是对的,这仅适用于单个编译单元
    【解决方案3】:

    很遗憾,如果您想在 std::any 实例的向量中找到 std::any 实例,答案是否定的。

    std::any 确实需要一些“魔法”,例如能够处理未知对象类型的创建,但这种机制是私有的,必须只支持对象创建而不支持相等比较。

    可以使用相同的方法实现您正在寻找的内容,但不能使用未发布所需详细信息的标准std::any。 “manager”模板需要枚举所有可能的操作,例如,在 g++ 实现中,它们是“access”、“get_type_info”、“clone”、“destroy”、“xfer”。

    variant 完全不同,因为明确列出了所有允许的类型,因此在任何使用它的地方都可以访问所有方法。

    【讨论】:

      【解决方案4】:

      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();
      }
      

      实际上它更好,因为正如 Kilian 指出的那样,variantoperator== 已经完全像这样工作了:

      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();
      }
      

      【讨论】:

      【解决方案5】:

      如果类型是intfloatstring(或有限的一组类型),您可以使用std::variantstd::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"; 
            }
          }
      }
      

      Live Demo

      【讨论】:

      • 一旦你使用std::variantstd::visit 可能会有所帮助。
      猜你喜欢
      • 2022-01-19
      • 2018-01-11
      • 2011-12-27
      • 1970-01-01
      • 2017-01-03
      • 2010-11-28
      • 2017-12-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多