使用C ++函数模板时出现转换错误

时间:2014-09-15 19:35:51

标签: c++ templates casting xcode5

这是我在这里的第一篇文章。实际上感觉有点尴尬; - )

我正在努力学习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&);

感谢您的评论

4 个答案:

答案 0 :(得分:2)

您收到此错误,因为对于std::string以外的实例化,您要将it->second std::string分配给val引用的变量float不可分配给字符串类型(intboolstd::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。你的函数会编译吗?不,因为valint,您将尝试将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;
}