【问题标题】:Converting to different data types based on different template arguments根据不同的模板参数转换为不同的数据类型
【发布时间】:2011-09-14 23:58:07
【问题描述】:

我需要在两个系统之间映射类型 - 源数据字段,可以是数字、字符或字符串,都存储为字符串对象;目标系统需要为每个数据字段的底层类型提供不同的数据类型,我需要动态地进行这种映射。

所以基本上,对于每个数据字段,我都有实际的字段字符串“s”和基础数据的类型“type”,并且我正在尝试根据“type”转换为“dest”类型。我一直在尝试使用模板和模板常量来破解可以做到这一点的东西,但没有运气。

我目前的尝试如下,但由于返回类型冲突,无法编译:

template<class CLASSTYPE, int CLASSID>
CLASSTYPE returnDifferentTypes()
{
using namespace std;

if (CLASSID == 1) // 1 = "string"
    return std::string("returned string");

if (CLASSID == 2) // 2 = int
    return 123;

if (CLASSID == 3) // 3 = double 
    return 123.123;
}

所以我一直在调用类似的东西

 string mapped = returnDifferentTypes<string, 1>()

or 
  int mapped = returnDifferentTypes<int, 2>()

谁能推荐一种更智能的清洁方法来做到这一点?理想情况下,我试图返回适当的返回类型,其中只包含一个表示要映射到的类型的字符串。提前致谢。

【问题讨论】:

    标签: c++ templates


    【解决方案1】:

    对于您的情况,CLASSID 是一个 冗余 参数;所以省略它。

    您可以简单地专门化方法returnDifferentTypes 用于不同的数据类型以获得更简洁的方式。

    template<class CLASSTYPE>
    CLASSTYPE returnDifferentTypes();  // undefined
    
    template<>
    std::string returnDifferentTypes<std::string>() {  // for 'std::string'
      return std::string("returned string");
    }
    template<>
    int returnDifferentTypes<int>() {  // for 'int'
      return 123;
    }
    template<>
    double returnDifferentTypes<double>() {  // for 'double'
      return 123.123;
    }
    

    用法:

    string mapped = returnDifferentTypes<string>();
    int mapped = returnDifferentTypes<int>();
    

    【讨论】:

    • 我通常对专门的功能模板持谨慎态度,可能另一种设计会更好。
    【解决方案2】:

    如果您可以静态识别 ID,则模板特化可能比 if-s 网络更好。

    template<int id>
    struct multireturn; // not defined
    
    template<>
    struct multireturn<1>
    { 
      typedef std::string return_type;
      return_type operator()() { return "string"; } 
    };
    
    template<>
    struct multireturn<2>
    { 
      typedef int return_type;
      return_type operator()() { return 12; } 
    };
    
    template<>
    struct multireturn<3>
    { 
       typedef double return_type;
       return_type operator()() { return 12.3; } 
    };
    
    ///call like this
    astring = multireturn<1>()()
    aint = multireturn<2>()()
    adouble = multireturn<3>()()
    

    同样,你可以使用函数重载:

    enum ret_string_t { ret_string; }
    enum ret_int_t { ret_int; }
    enum ret_ouble_t { ret_double; }
    
    std::string multireturn(ret_string_t) { return "string"; }
    int multireturn(ret_int_t) { return 12; }
    int multireturn(ret_double_t) { return 12.34; }
    
    /// call as:
    astring = multireturn(ret_string);
    aint= multireturn(ret_intg);
    adouble= multireturn(ret_double);
    

    或者 - 不那么正统,但仍然有效 -

    struct multireturn
    {
      operator std::string() { return "text"; }
      operator int() { return 10; }
      operator doble() { return 3.5; }
    };
    
    ///call as:
    astring = multireturn();
    aint = multireturn();
    adouble = multireturn();
    

    当然,const 正确性必须根据所有示例的要求进行调整。

    【讨论】:

    • +1 哇,有很多我没有想到的好选择。我认为最后一个是最好的。
    • +1 这太好了,我最终使用了整数特化方法,因为我想关闭传递给函数的参数以确定返回类型。
    【解决方案3】:

    除非您使用某种类型的 Variant 类而不是 string 来存储所有内容,否则您的代码中的某些时候需要有一种在类型之间进行选择的分支形式。但是,转换函数本身并不是它的地方。转换函数只能返回一种类型。如果您尝试在其中分支,它将需要返回三种类型。同样,除非您使用 Variant 或其他一些丑陋的 hack(例如 void*),否则您将永远无法使用它。

    假设你接受在某个地方有一个区分类型的分支,这里不需要函数模板专门化。一个简单的CvtTo 函数模板就可以完成这项工作:

    #include <sstream>
    #include <string>
    
    template<class T> T CvtTo(const std::string& s)
    {
      stringstream ss;
      ss << s;
      T ret;
      ss >> ret;
      return ret;
    }
    

    现在完全满足您对分支逻辑的需求,并在您即将存储数据时致电CvtTo:

    // SOME DATABASE FUNCTION N STUFF
    
    std::string value = ...; // VALUE TO BE CONVERTED & STORED
    
    enum FieldType {String, Int, Float};
    FieldType field_type = ...;
    
    switch( field_type )
    {
    case String :
      store_a_string(CvtTo<std::string>(value));  // note this is not strictly necesarry for String
      break;
    
    case Int :
      store_an_int(CvtTo<int>(value));
      break;
    
    case Float :
      store_a_float(CvtTo<float>(value));
      break;
    }
    // FURTHER MAGIC...
    

    【讨论】:

      【解决方案4】:

      如果您真的想要一个可以返回多种类型的函数,您正在寻找boost::variant 或者可能是boost::any。

      但是,请至少花点时间考虑一下您为什么需要这种行为,以及是否有替代方法可以消除从同一函数返回多个不同类型的需要。

      【讨论】:

        【解决方案5】:

        如果您想在字符串之间转换数值或其他“iostream 可序列化”类型,您可以使用boost::lexical_cast。

        【讨论】:

          猜你喜欢
          • 2019-11-10
          • 2020-05-08
          • 1970-01-01
          • 1970-01-01
          • 2012-12-25
          • 2013-04-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多