这是我在这里的第一篇文章。实际上感觉有点尴尬; - )
我正在努力学习C ++模板。我有一个类'textSettings"
,它读取TXT文件中的值对,每个都在它的行上,并将它们放在std :: map中,它使用std :: string作为键和值。
文本文件中可能的入口可以是例如GRID_SIZE 6
,因此映射的键变为字符串"GRID_SIZE"
,值为字符串"6"
。
在我的程序中,我想将它们用作实际值的类型:int/float/bool/string
。
程序只希望将这4种类型作为输入。因此,我使用模板化函数通过其键在地图中查找某个值,并仅为这些类型实例化它。
在此功能中,我使用std::tr1::is_same
检查类型,以便能够正确解析值。
它在else子句中失败,其中XCode抱怨,抛出三个错误。它不能将val转换为float或int或bool。错误看起来都一样:
/src/textSettings.cpp:82:17:分配给' int'来自不兼容的类型 '的std :: string' (又名' basic_string') /src/textSettings.cpp:82:17:分配给' float'来自不兼容的类型 '的std :: string' (又名' basic_string') /src/textSettings.cpp:82:17:分配给' bool'来自不兼容的类型 '的std :: string' (又名' basic_string')
然而,所有抱怨的类型都已由其他if
条款处理。我真的不知道如何处理这件事。我还在学习C ++和模板,所以我可能会忽略一些明显的东西。
如果我对该行进行评论,该程序将完美地编译和链接。显然,我无法解析文本文件中的值......
我正在使用XCode 5.1.1
textSettings.h
template<typename T>
bool findValue(const std::string& key, T& val);
textSetting.cpp
template<typename T>
bool textSettings::findValue(const std::string &key, T& val) {
std::map<std::string, std::string>::iterator it;
it = data.find(key);
if (it != data.end()) {
if (std::tr1::is_same<T, int>::value) {
val = atoi(it->second.c_str());
}
else if (std::tr1::is_same<T, float>::value) {
val = atof(it->second.c_str());
}
else if (std::tr1::is_same<T, bool>::value) {
val = static_cast<bool>(atoi(it->second.c_str()));
}
else if (std::tr1::is_same<T, std::string>::value) {
val = it->second; // <- ERROR HERE
}
else {
printf("Textsettings:: Error, type unknown!\n");
return false;
}
return true;
}
return false;
}
template bool textSettings::findValue<int>(const std::string&, int&);
template bool textSettings::findValue<float>(const std::string&, float&);
template bool textSettings::findValue<bool>(const std::string&, bool&);
template bool textSettings::findValue<std::string>(const std::string&, std::string&);
感谢您的评论
答案 0 :(得分:2)
您收到此错误,因为对于std::string
以外的实例化,您要将it->second
std::string
分配给val
引用的变量float
不可分配给字符串类型(int
,bool
或std::tr1::is_same<T, std::string>::value
)。
您的条件false
将评估为bool ParseStr(const std::string& str, int& val) {
val = atoi(str.c_str());
return true;
}
bool ParseStr(const std::string& str, float& val) {
val = atof(str.c_str());
return true;
}
bool ParseStr(const std::string& str, bool& val) {
val = static_cast<bool>(atoi(str.c_str()));
return true;
}
bool ParseStr(const std::string& str, std::string& val) {
val = str;
return true;
}
template <typename T>
bool ParseStr(const std::string& str, T& val) {
printf("Textsettings:: Error, type unknown!\n");
return false;
}
template<typename T>
bool textSettings::findValue(const std::string &key, T& val) {
std::map<std::string, std::string>::iterator it;
it = data.find(key);
if (it != data.end()) {
return ParseStr(it->second, val);
}
return false;
}
的事实并不意味着您没有义务在条件块中提供词法正确的代码。
通过为每个所需类型创建单独的函数重载,并使用重载函数调用自动匹配所需类型,可以更好地解决此类问题:
{{1}}
答案 1 :(得分:1)
在每个模板实例化中,只有一个if
分支在语义上是正确的,并且编译器能够生成适当的实例化代码,而所有其他分支仍然是完全错误的。
看一下下面的代码,你所做的错误就是一样:
{
int a = 33; // Assume this code was into a template and 'int = T'
if(std::is_same<decltype(a), int>::value)
a = 44; // Okay
else
a = "hello"; // How can this compile?
}
由于您明确地为所有这些类型实例化了模板化函数,因此代码实际生成,因此您就会遇到错误。
有几个解决方案,但一个好的方法可能是专门化你的功能(记住:功能不能部分专业化)
template<typename T>
bool textSettings::findValue(const std::string &key, T& val) {
// type not recognized, assert or handle
...
return false;
}
template<> // Specialized for int
bool textSettings::findValue<int>(const std::string &key, int& val) {
std::map<std::string, std::string>::iterator it;
it = data.find(key);
if (it != data.end()) {
val = atoi(it->second.c_str());
return true;
}
return false;
}
// all the others..
template bool textSettings::findValue<int>(const std::string&, int&);
template bool textSettings::findValue<float>(const std::string&, float&);
template bool textSettings::findValue<bool>(const std::string&, bool&);
template bool textSettings::findValue<std::string>(const std::string&, std::string&);
答案 2 :(得分:1)
我认为你误解了类型特征使用的概念。类型特征在编译期间被计算为常量表达式,并由在运行时期间计算的常量值替换。因此,在T=int
的示例中,以下是正在评估的等效模板实例代码
bool textSettings::findValue(const std::string &key, int& val) {
std::map<std::string, std::string>::iterator it;
it = data.find(key);
if (it != data.end()) {
if (true) {
val = atoi(it->second.c_str());
}
else if (false) {
val = atof(it->second.c_str());
}
else if (false) {
val = static_cast<bool>(atoi(it->second.c_str()));
}
else if (false) {
val = it->second; // <- ERROR HERE
}
else {
printf("Textsettings:: Error, type unknown!\n");
return false;
}
return true;
}
return false;
}
正如您所看到的,正在编译的行仍在编译,但本身在类型int
的上下文中,它是一个无效的表达式,其中字符串被分配给整数。
类型特征在模板评估期间可能会有所帮助,但在这种特殊情况下,模板特化最好用于根据类型选择代码。
template<typename T>
bool textSettings::findValue(const std::string &key, T& val) {
printf("Textsettings:: Error, type unknown!\n");
return false;
}
template<>
bool textSettings::findValue(const std::string &key, std::string& val) {
std::map<std::string, std::string>::iterator it;
it = data.find(key);
if (it != data.end()) {
val = it->second; // <- ERROR HERE
return true;
}
return false;
}
template<>
bool textSettings::findValue(const std::string &key, int& val) {
std::map<std::string, std::string>::iterator it;
it = data.find(key);
if (it != data.end()) {
val = atoi(it->second.c_str());
return true;
}
return false;
}
template<>
bool textSettings::findValue(const std::string &key, float& val) {
std::map<std::string, std::string>::iterator it;
it = data.find(key);
if (it != data.end()) {
val = atof(it->second.c_str());
return true;
}
return false;
}
template<>
bool textSettings::findValue(const std::string &key, bool& val) {
std::map<std::string, std::string>::iterator it;
it = data.find(key);
if (it != data.end()) {
val = static_cast<bool>(atoi(it->second.c_str()));
return true;
}
return false;
}
答案 3 :(得分:1)
您需要使用基于所谓标记分派的解决方案。最流行的例子是实现std :: advance。这是一个example。
您的代码中的问题是编译器将尝试为任何给定类型T
实例化您的函数。想象一下,您正在使用T = int
调用函数。然后将所有T
替换为int
。你的函数会编译吗?不,因为val
将int
,您将尝试将std::string
分配给int
,并且没有隐式转化。
这是您的code live: 您只需要下一个技巧(请参阅下面的标签调度链接):
bool dispatcher(const std::string& str, int& val) {
val = atoi(str.c_str());
return true;
}
bool dispatcher(const std::string& str, float& val) {
val = atof(str.c_str());
return true;
}
bool dispatcher(const std::string& str, bool& val) {
val = static_cast<bool>(atoi(str.c_str()));
return true;
}
bool dispatcher(const std::string& str, std::string& val) {
val = str;
return true;
}
bool dispatcher(const std::string& str, ...) {
printf("Textsettings:: Error, type unknown!\n");
return false;
}
template<typename T>
bool findValue(const std::string &key, T& val) {
std::map<std::string, std::string>::iterator it;
it = data.find(key);
if (it != data.end())
return dispatcher(it->second, val);
return false;
}