【问题标题】:How to use template function to set values with different types?如何使用模板函数设置不同类型的值?
【发布时间】:2012-09-26 16:16:20
【问题描述】:

大家!

我有一个名为 UpdateValue 的模板函数,它旨在更新一些不同类型的值,例如整数、BOOL、字符串等。请参考以下代码sn-p:

#include <typeinfo.h>

template<class T> 
void UpdateValue(T Value)
{   
    if ( typeid(int) == typeid(Value) ) 
    {  
       ZOrder(Value);
    }
    else if ( typeid(bool) == typeid(Value) )
    {  
       BOOL bShow = Value ? TRUE : FALSE;
       Show(bShow);
    }
    else if ( typeid(CString) == typeid(Value) )
    {  
       Theme(Value);
    }
}

void Show(BOOL bShow) { m_bShow = bShow; }
void ZOrder(int nZOrder) { m_nZOrder = nZOrder; }
void Theme(CString strTheme) { m_strTheme = strTheme; }

BOOL m_bShow;
int m_nZOrder;
CString m_strTheme;

但是当我使用下面的语句来调用这种模板函数时

CString strValue = _T("Animal");
UpdateValue<CString>(strValue);

上述代码编译失败,抛出以下异常:

e:\dynaprop\dynaprop\mainfrm.cpp(269) : 错误 C2664: 'CMainFrame::ZOrder' : 无法将参数 1 从 'CString' 转换为 'int' 1> 没有可以执行此转换的用户定义转换运算符,或者无法调用该运算符 1> e:\dynaprop\dynaprop\mainfrm.cpp(67) : 请参阅正在编译的函数模板实例化 'void CMainFrame::UpdateValue(T)' 的参考 1> 与 1> [ 1> T=C字符串 1>]

你能告诉我怎么解决吗?提前谢谢!

【问题讨论】:

  • ValueCStringZOrderint 时,您调用ZOrder(Value)
  • 谢谢chris。typeid不是在模板函数中生效吗?当我使用 UpdateValue("Animal") 通过调用 Theme 函数修改字符串时,我想使用“if ... then”条件块以及 typeid 来防止调用 ZOrder 和 Show 函数。
  • 你可能想研究模板专业化。
  • @GoldenLee,typeid 是运行时操作,以及基于if 的代码。编译器解析每个运行时方面,因此只有编译时比较才能阻止它查看该调用。模板专业化很可能是您想要的。
  • @chris:谢谢。我在理解 typeid 的运行时机制时犯了一个错误。我可能会使用重载函数来解决我的问题。

标签: c++ function templates


【解决方案1】:

您并没有真正使用模板工具。或者宁愿错过使用它。发生的情况是您将ZOrder(Value) 称为void UpdateValue(T Value) 实例化,其中T 的每次出现都按照您的指定替换为CString。但仅此而已。

函数如下所示

void UpdateValue(CString Value)
{   
    if ( typeid(int) == typeid(Value) ) 
    {  
       ZOrder(Value);
    }
    else if ( typeid(bool) == typeid(Value) )
    {  
       BOOL bShow = Value ? TRUE : FALSE;
       Show(bShow);
    }
    else if ( typeid(CString) == typeid(Value) )
    {  
       Theme(Value);
    }
}

在模板被实例化后,会发生正常的编译工作......你会得到一个错误,因为它写成ZOrder(Value);,其中valueCString 类型。这将产生错误,即使在这种情况下使用 RTTI 时,此调用路径也不应该到达。

你应该做的是使用特化来处理不同的类型。尝试用这些替换 UpdateValue 的原始定义:

template<> // specialization when T is get to be int
void UpdateValue<int>(int Value) { ZOrder(Value);};

template<> // specialization when T is get to be CString
void UpdateValue<bool>(bool Value) { BOOL bShow = Value ? TRUE : FALSE; Show(bShow);};

template<> // specialization when T is get to be CString
void UpdateValue<CString>(CString Value) { Theme(Value);};

template<class T>  //general case
void UpdateValue(T Value) { cout << "IMPLEMENT ME\n";};

然后,当您调用 UpdateValue&lt; T &gt; 时,您会调用带有适当正文的版本。您也不必使用 RTTI。

更新:在评论者建议之后。如果您不需要/不想处理一般情况(即未知类型),您可以诉诸普通重载并使用:

//Value is int
void UpdateValue(int Value) { ZOrder(Value);};
//Value is bool
void UpdateValue(bool Value) { BOOL bShow = Value ? TRUE : FALSE; Show(bShow);};
//Value is CString
void UpdateValue(CString Value){ Theme(Value);};

那时甚至不需要模板!

【讨论】:

  • 谢谢luk32!我懂了。但是还有其他解决方案吗?
  • @GoldenLee 使用重载而不是模板。有关示例,请参见其他答案。
  • @luk32:我会采纳你的建议,改用重载。谢谢!
  • @GoldenLee 好吧,这是 Rost 的建议,我更新了我的答案,使其尽可能完整。
【解决方案2】:

正如在其他一些答案中所述(或暗示),您需要将您的三个代码分支分成三个模板专业化 - 每个参数类型一个。但这很愚蠢;如果您真的想要一个 UpdateValue 函数根据参数类型执行三种不同的操作,只需编写 UpdateValue 的三个重载并且根本不使用模板!

(这可能无法使用BOOL 类型,因为它也可能是int。您很可能应该使用bool,除非有一些非常强大且奇怪的理由不这样做。 )

【讨论】:

    【解决方案3】:

    我认为您可以使用函数重载来实现您想要做的事情, (我将其更改为使用 STL 字符串,以便可以对其进行编译):

    #include <typeinfo>
    #include <string>
    
    using namespace std;
    
    bool m_bShow;
    int m_nZOrder;
    string m_strTheme;
    
    void Show(bool bShow) { m_bShow = bShow; }
    void ZOrder(int nZOrder) { m_nZOrder = nZOrder; }
    void Theme(string strTheme) { m_strTheme = strTheme; }
    
    void DoUpdate( bool bShow )
    {
        Show( bShow );
    }
    
    void DoUpdate( int nZOrder )
    {
        ZOrder( nZOrder );
    }
    
    void DoUpdate( string strTheme )
    {
        Theme( strTheme );
    }
    
    template<class T>
    void UpdateValue(T Value)
    {
        DoUpdate( Value );
    }
    
    int main( int argc, char **argv )
    {
        string strValue = "Animal";
        UpdateValue<string>(strValue);
    
        return 0;
    }
    

    【讨论】:

    • 这里为什么需要模板?这是无用的和过度的。重载就够了。显式参数类型调用也很奇怪 - 让编译器通过参数类型推导来完成它的工作。
    • @nemasu:谢谢!我只是使用最简单的代码来展示我的问题。我的生产代码非常复杂。如果我为每种数据类型编写一个 DoUpdate 函数,就会编写很多重复的代码。无论如何,非常感谢!
    • @Rost 显然,您不需要包含这么短代码的模板,但我感觉这不是整个程序。我只是就如何在使用现有代码时解决问题提供建议。
    • @nemasu:是的!感谢您抽出宝贵的时间。
    【解决方案4】:

    代码仍然被编译,即使它是死代码。所以你说:

    if ( typeid(int) == typeid(Value) )
    {  
       ZOrder(Value);
    }
    

    它仍然会尝试编译代码,即使它永远不会执行,所以它会抛出错误。

    最好使用模板专业化:

    template<class T> 
    void UpdateValue(T Value) {
        // By default, do nothing.
    }
    
    void UpdateValue < int >(int value) {
        ZOrder(value);
    }
    
    void UpdateValue < bool >(bool value) {
        BOOL bShow = Value ? TRUE : FALSE;
        Show(bShow);
    }
    
    void UpdateValue < CString >(CString value) {
        Theme(value);
    }
    

    总而言之,这是对模板的不必要使用。你最好改用几个重载。

    【讨论】:

      【解决方案5】:

      正如之前的答案和 cmets 提到的,UpdateValue&lt;CString&gt;(CString) 实例化失败,因为它无法编译 ZOrder(CString) 表达式。请记住,模板实例化只是编译时类型替换,而不是运行时。混合运行时 RTTI 和模板并不是一个好主意,而且看起来像模板滥用。

      建议使用函数重载而不是模板,它会产生相同的结果:

      void UpdateValue(int Value)
      {
          ZOrder(Value);
      }     
      
      void UpdateValue(bool Value)
      {
         BOOL bShow = Value ? TRUE : FALSE;        
         Show(bShow);     
      }
      
      void UpdateValue(const CString& Value)
      {
         Theme(Value);
      } 
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多