【问题标题】:std::map with different data types for valuesstd::map 具有不同数据类型的值
【发布时间】:2017-11-21 03:48:25
【问题描述】:

我一直在搜索论坛和谷歌,很难理解如何做我想做的事。

我的示例基于您在选举中看到的典型数据集。我想拆分一个分隔字符串并创建一个地图以供以后访问 字符串如下所示: "name=candidate1;vote=1000;percent=10.5"

我可以按如下方式创建我的字符串映射

    while (getline(oss, key, '=') && getline(oss, value))
    {

      mCanData.insert(std::pair<std::string, std::string>(key, value));

    }

我想做的,我不知道这是否可能,是在地图中插入具有不同数据类型的值(即key = "name" value ="candidate1", key = "vote" value =1000,键=“百分比”值=10.5)。我要创建的地图将设置一个私有类变量,稍后其他类可以通过 getter 访问该变量。我无法使用 boost 库,所以请不要这样做。

任何帮助都会很棒,因为我现在迷路了。如果有更好的方法来解决这个问题,我也想知道。

【问题讨论】:

  • 你怎么知道每个值应该有什么类型?您是否要记住 vote 始终是 unsigned int,还是要尝试解析它并注意到它完全是数字并且没有小数点?
  • 有更好的方法来做你正在尝试的事情,但不清楚你真正想要实现的目标。 编译时间发生在运行时间之前。如果运行时字符串决定了你得到的类型,编译器就不会知道它是什么类型。它将无法生成正确的代码。
  • 我认为您需要重新审视您组织数据的方式。像这样混音让我头疼不已。另外我做错了:(
  • 不同的数据类型都可以在编译时决定。 Name 永远是一个字符串,vote 永远是一个 int,等等
  • 谢谢大家,这很有帮助

标签: c++ dictionary stdmap


【解决方案1】:

如果您真的想在地图中放入非结构化数据,在 C++17 中,您可以使用 std::variant 来执行此操作,然后访问它来取回您的数据。
它遵循一个最小的工作示例:

#include <variant>
#include <string>
#include <map>
#include <iostream>

int main() {
    std::map<std::string, std::variant<std::string, int, double>> mm;
    mm["name"] = "candidate1";
    mm["vote"] = 1000;
    mm["percent"] = 10.5;

    auto visitor = [](auto data){ std::cout << data << std::endl; };
    std::visit(visitor, mm["name"]);
    std::visit(visitor, mm["vote"]);
    std::visit(visitor, mm["percent"]);
}

wandbox 上查看并运行它。
它是否适合您主要取决于您是否可以使用 C++17。你没有具体说明,所以很难说。


话虽如此,结构化数据 (as suggested by @rici) 似乎是解决问题的更好方法。
但是,我们既不能说真正的问题是什么,也不能说你是如何设计其余代码的,所以值得一提的是std::variant

【讨论】:

    【解决方案2】:

    在 C++ std::map 中,所有值都具有相同的类型。

    一般来说,当你有这样的结构化数据时,你想定义一个结构化类型:

    class Vote {
      public:
        std::string name;
        int         vote;
        double      percent;
    };
    

    【讨论】:

      【解决方案3】:

      正如 Rici 上面提到的,正确的方法应该是拥有一个用户定义的类型,例如类或结构,并将其用作键。在您的情况下,您甚至可以只使用 set 而不是 map。请记住为您的用户定义类型提供比较器函数或

      struct Vote {
          std::string name;
          int         vote;
          double      percent;
      };
      
      struct myComparator
      {
          bool operator() (const Vote& obj1, const Vote& obj2)
          {
            // Write your comparison logic which will be used by your container while inserting the data
          }
      };
      
      int main()
      {
          std::set<Vote, myComparator> mySet;
          // Do your insertion and other operations.
          return 0;
      }
      

      【讨论】:

        【解决方案4】:

        在没有 C++ 版本依赖的情况下试试这个:

        #include <iostream>
        #include <map>
        #include <cstdlib>
        
        
        class Vote {
        public:
            enum DATA_TYPE
            {
                STRING,
                INT,
                DOUBLE
            };
            Vote(int value)
                : m_type(DATA_TYPE::INT), m_ptr(new int(value)){}
            Vote(std::string& value)
                : m_type(DATA_TYPE::STRING), m_ptr(new std::string(value)){}
            Vote(double value)
                : m_type(DATA_TYPE::DOUBLE), m_ptr(new double(value)){}
            int toInt() {
                switch (m_type) {
                case DATA_TYPE::INT:
                    return *(static_cast<int*>(m_ptr));
                    break;
                case DATA_TYPE::DOUBLE:
                    return (int)*(static_cast<double*>(m_ptr));
                    break;
                case DATA_TYPE::STRING:
                    return string2int(*(static_cast<std::string*>(m_ptr)));
                }
            }
            double toDouble() {
                switch (m_type) {
                case DATA_TYPE::INT:
                    return (double)*(static_cast<int*>(m_ptr));
                    break;
                case DATA_TYPE::DOUBLE:
                    return *(static_cast<double*>(m_ptr));
                    break;
                case DATA_TYPE::STRING:
                    return string2double(*(static_cast<std::string*>(m_ptr)));
                }
            }
            std::string toString() {
                switch (m_type) {
                case DATA_TYPE::INT:
                    return int2string(*(static_cast<int*>(m_ptr)));
                    break;
                case DATA_TYPE::DOUBLE:
                    return double2string(*(static_cast<double*>(m_ptr)));
                    break;
                case DATA_TYPE::STRING:
                    return *(static_cast<std::string*>(m_ptr));
                }
            }
            int string2int(std::string str)
            {
                return atoi(str.c_str());
            }
            double string2double(std::string str)
            {
                return atof(str.c_str());
            }
            std::string int2string(int value)
            {
                char buffer[10];
                itoa(value, buffer, 10);
                return std::string(buffer);
            }
            std::string double2string(double value)
            {
                char buffer[24];
                sprintf(buffer,"%f", value);
                return std::string(buffer);
            }
            DATA_TYPE type()
            {
                return m_type;
            }
        private:
            std::string m_strValue;
            int m_intValue;
            double m_douValue;
            DATA_TYPE m_type;
            void *m_ptr;
        };
        
        int main()
        {
        
            std::map<std::string, Vote> map;
            map.insert(std::pair<std::string, Vote>("Data1", 2));
            map.insert(std::pair<std::string, Vote>("Data2", 2.2));
            std::string Data3Value("1.31232");
            map.insert(std::pair<std::string, Vote>("Data3", Data3Value));
        
            for (std::map<std::string, Vote>::iterator it_map = map.begin();
                 it_map != map.end();
                 it_map ++) {
                Vote::DATA_TYPE type = (*it_map).second.type();
                switch (type) {
                case Vote::DATA_TYPE::INT:
                    std::cout << "Key: " << (*it_map).first << ", value: " << (*it_map).second.toInt() << std::endl;
                    std::cout << "Key: " << (*it_map).first << ", value: " << (*it_map).second.toDouble() << std::endl;
                    std::cout << "Key: " << (*it_map).first << ", value: " << (*it_map).second.toString() << std::endl;
                    break;
                case Vote::DATA_TYPE::DOUBLE:
                    std::cout << "Key: " << (*it_map).first << ", value: " << (*it_map).second.toInt() << std::endl;
                    std::cout << "Key: " << (*it_map).first << ", value: " << (*it_map).second.toDouble() << std::endl;
                    std::cout << "Key: " << (*it_map).first << ", value: " << (*it_map).second.toString() << std::endl;
                    break;
                case Vote::DATA_TYPE::STRING:
                    std::cout << "Key: " << (*it_map).first << ", value: " << (*it_map).second.toInt() << std::endl;
                    std::cout << "Key: " << (*it_map).first << ", value: " << (*it_map).second.toDouble() << std::endl;
                    std::cout << "Key: " << (*it_map).first << ", value: " << (*it_map).second.toString() << std::endl;
                    break;
                }
        
            }
            return 0;
        }
        

        你可以提供3种类型的构造器,然后你可以接受3种类型来构造对象,然后提供一个void指针,指向我们在构造对象时“新建”的数据。当我们调用 toInt()、toDouble()、toString() 函数并使用 static_cast 将指针转换为我们想要的数据类型时。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-04-23
          • 2018-11-08
          • 1970-01-01
          • 2021-09-25
          • 1970-01-01
          • 2014-09-02
          相关资源
          最近更新 更多