首页 > 解决方案 > 如何在模板类中仅在一种类型的 Vector 中插入数据?

问题描述

我有一个 FileParser 类,如下所示。该类包含一个公共方法,该方法通过解析各个文件的数据来解析类型 T 的向量。文件路径作为参数传递。有两个文件可以读取 Operations.txt 和 SetupDuration.txt。要读取特定文件,我将其与字符串操作和 setupDuration 进行比较。数据被解析为类变量并被推送到传递给函数的向量中。

template <class T>
class FileParser
{       
public:
    vector<T> parseFile(vector<T> vec, string filePath)
    {
        ifstream file;
        file.open(filePath);

    
        string operations = "Operations";
        string setupDuration = "SetupDuration";
        string str; 
        
        // if Operations is found in filepath execute this if statement
        if (filePath.find(operations) != string::npos)  
        {
            Operation op;            
            while (getline(file, str))
            {
                // parsing file data for operation file , didn't include code for it
                op.codeOne = "...."; //assigning parsed data
                op.codeTwo = "....";
                vec.push_back(op);
            }
        }
        // if SetupDuration is found in filepath execute this if statement
        else if(filePath.find(setupDuration) != string::npos)
        {
            SetupDuration sd;

            
            while (getline(file, str))
            {
                // parsing file data of SetupDuration.txt file , didn't include code for it
                sd.title = "...."; //assigning parsed data
                sd.body = "....."; //assigning parsed data
                vec.push_back(sd);
            }
        }          

    file.close();
    return vec;
};

在我的主要课程中:

int main()
{
    vector<Operation> operations;
    vector<SetupDuration> setupDur;
    string opFilePath = "./Operations.txt";
    string setupDurFilePath = "./SetupDuration.txt";

    FileParser<Operation> fp;
    FileParser<SetupDuration> sFp;
    operations = fp.parseFile(operations,opFilePath);
    setupDur = sFp.parseFile(setupDur,setupDurFilePath);
}

我面临的问题是,当我推回向量时,它推回向量意味着推回操作操作向量,它还尝试推回 SetupDuration 向量。

no matching function for call to 'std::vector<Operation>::push_back(SetupDuration&)
no matching function for call to 'std::vector<SetupDuration>::push_back(Operation&)
note:   no known conversion for argument 1 from 'Operation' to 'std::vector<SetupDuration>::value_type&& {aka SetupDuration&&}'
note:   no known conversion for argument 1 from 'SetupDuration' to 'std::vector<Operation>::value_type&& {aka Operation&&}'

解决这个问题的一种方法是不使用模板类并制作单独的函数,但我真的想知道模板类是否可以做到这一点。有什么方法可以以正确的方式实现吗?

标签: c++templatesgenerics

解决方案


首先,我不确定您是否真的需要按值传递向量作为输入参数。

  • 如果向量始终为空,那么您也可以在类本身内创建它。我假设对于以下示例代码。
  • 如果您需要附加到它,然后通过引用传递它std::vector<T>&
  • 只有当您需要创建它的副本时,我才会真正按值传递它(就像您所做的那样)或 asstd::vector<T> const&并创建它的副本。

其次,我不确定您的类是应该是模板类还是parseFile应该只模板化方法。

此外,如果您不需要文件解析器类本身来创建它及其任何非静态类成员,您可能想要创建该方法。staticstd::vector<T>

无论如何,这些可能是您可以解决遇到的问题的最佳选择。

  • 如果你可以用 C++17 编译你的代码,你可以重写你的代码以constexpr if结合std::is_same_vtype trait fromtype_traits使用,看起来像这样:

    template <class T>
    class FileParser {       
      public:
        static std::vector<T> parse(std::string const& file_path) const {
          std::vector<T> vec = {};
          // Code for all templates goes here
    
          if constexpr (std::is_same_v<T,Operation>) {
            // Code for Operation parsing here
          } else if constexpr (std::is_same_v<T,SetupDuration>) {
            // Code for SetupDuration parsing goes here
          }
          // else if constexpr ... Other template types...
          // else ... Error handling
    
          // Code for all templates goes here
          return vec;
        }
    };
    

    这样可以避免您遇到的编译错误。如果你想确保filename仍然有这种形式,你可以添加一个断言,例如assert(file_path.find("Operations") != string::npos).

    在这里试试!

  • 如果您不需要模板化整个类,您可以单独为所述类型专门化方法parse(注意:您不能只专门化模板类的方法!)。这不需要 C++17:

    class FileParser {       
      public:
        template <typename T>
        static std::vector<T> parse(std::string const& file_path) const;
        // Other methods and class members
    };
    
    template <>
    static std::vector<Operation> FileParser::parse<Operation>(std::string const& file_path) const {
      std::vector<Operation> vec = {};
      // Code for Operation parsing does here
      return vec;
    }
    
    template <>
    static std::vector<SetupDuration> FileParser::parse<SetupDuration>(std::string const& file_path) const {
      std::vector<SetupDuration> vec = {};
      // Code for SetupDuration parsing does here
      return vec;
    }
    

    在这里试试!

  • 或者,您可以从文件流中为每个模板类型编写一个构造函数,以便您实际上用while (getline(file, str)) ...解析器T::parse(std::ifstream&)(forOperationSetupDuration)替换该部分,以便所有特定类型都由类本身处理。我认为从软件设计的角度来看,这是最好的解决方案:

    template <class T>
    class FileParser {       
      public:
        static std::vector<T> parse(std::string const& file_path) const {
          std::ifstream ifs {};
          ifs.open(file_path);
          std::vector<T> vec = T::parse(ifs);
          return vec;
        }
    };
    

    在这里试试

    根据文件的形式,您可能会将文件分解为仅包含单个元素信息的较小块(例如,如果一行对应于一个元素)并为处理解析std::string的类创建构造函数。T(std::string const&)在这种情况下,你可以做类似的事情

    template <class T>
    class FileParser {       
      public:
        static std::vector<T> parse(std::string const& file_path) const {
          std::ifstream ifs {};
          ifs.open(file_path);
          std::vector<T> vec = {};
          // Assuming a singleline per element regardless of the container type
          // If this is not the case you could split it up in chunks depending on the template with if constexpr
          std::string line {};
          while (std::getline(ifs, line)) {
            // Short for vec.push_back(T{line});
            vec.emplace_back(line);
          }
          return vec;
        }
    };
    

    在这里试试!


推荐阅读