c++ - 如何在模板类中仅在一种类型的 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&&}'
解决这个问题的一种方法是不使用模板类并制作单独的函数,但我真的想知道模板类是否可以做到这一点。有什么方法可以以正确的方式实现吗?
解决方案
首先,我不确定您是否真的需要按值传递向量作为输入参数。
- 如果向量始终为空,那么您也可以在类本身内创建它。我假设对于以下示例代码。
- 如果您需要附加到它,然后通过引用传递它
std::vector<T>&
。 - 只有当您需要创建它的副本时,我才会真正按值传递它(就像您所做的那样)或 as
std::vector<T> const&
并创建它的副本。
其次,我不确定您的类是应该是模板类还是parseFile
应该只模板化方法。
此外,如果您不需要文件解析器类本身来创建它及其任何非静态类成员,您可能想要创建该方法。static
std::vector<T>
无论如何,这些可能是您可以解决遇到的问题的最佳选择。
如果你可以用 C++17 编译你的代码,你可以重写你的代码以
constexpr if
结合std::is_same_v
type 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&)
(forOperation
和SetupDuration
)替换该部分,以便所有特定类型都由类本身处理。我认为从软件设计的角度来看,这是最好的解决方案: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; } };