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

Posted

技术标签:

【中文标题】使用 C++ 函数模板时的转换错误【英文标题】:Casting errors when using C++ function templates 【发布时间】:2014-11-09 10:15:36 【问题描述】:

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

我正在为 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&);

感谢您的cmets

【问题讨论】:

【参考方案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;

【讨论】:

【参考方案2】:

问题

在每个模板实例化中,只有一个 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&);

【讨论】:

【参考方案3】:

我认为您误解了类型特征使用的概念。类型特征在编译期间被评估为常量表达式,并被在运行时评估的常量值替换。因此,在您的 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;

【讨论】:

【参考方案4】:

您收到此错误,因为对于 std::string 以外的实例化,您将 it-&gt;second(即 std::string)分配给 val 引用的变量,该变量不可分配给字符串类型(@ 987654325@、intbool)。

您的条件std::tr1::is_same&lt;T, std::string&gt;::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;
 

【讨论】:

谢谢!清洁溶液。

以上是关于使用 C++ 函数模板时的转换错误的主要内容,如果未能解决你的问题,请参考以下文章

C++中函数模板和模板函数的区别

C++ 提高教程 模板-普通函数雨函数模板区别

C++ 函数重载,函数模板和函数模板重载,选择哪一个?

C++函数模板和普通函数的调用规则

C++ Primer 5th笔记(chap 16 模板和泛型编程)模板实参推断

错误 C2893:无法专门化函数模板 C++