【问题标题】:Changing return type of a function without template specialization. C++在没有模板特化的情况下更改函数的返回类型。 C++
【发布时间】:2010-11-03 21:02:37
【问题描述】:

我想知道是否可以根据分配给它的变量类型来更改函数的返回类型。这是我的意思的一个简单示例。

我想创建一个函数来解析字符串中的 int、bool 或 float 变量。比如……

Int value = parse("37");
Float value = parse("3.14");
Bool value = parse("true");

我知道如果我将此函数设为模板,则必须从始终为字符串的参数列表中确定变量类型。有没有其他方法可以用 c++ 做到这一点?

【问题讨论】:

    标签: c++ templates return


    【解决方案1】:

    这可以通过转换函数来完成

    struct proxy {
        string str;
        proxy(string const &str):str(str) { }
        template<typename T> operator T() { 
            return boost::lexical_cast<T>(str); 
        }
    };
    
    proxy parse(string const &str) { return proxy(str); }
    

    现在你只需要做

    float a = parse("3.1");
    

    它应该运作良好。顺便说一句,您可以直接使用该类。我建议将其重命名为 conversion_proxy 以表明它只是一个正在发生的转换的代理,但它本身并不进行转换

    struct conversion_proxy {
        string str;
        conversion_proxy(string const &str):str(str) { }
        template<typename T> operator T() { 
            return boost::lexical_cast<T>(str); 
        }
    };
    
    float a = conversion_proxy("3.1"); 
    

    【讨论】:

    • 这看起来有点不对劲。您没有调用 () 运算符,只是调用 ctor。我没有投票,因为我不确定。请澄清。
    • 我不打算调用 op() :) 我调用了一个返回代理的函数。在第二部分中,我现在建议直接使用该类,中间不使用干预函数
    • 这正是我正在寻找的答案!非常感谢。不过我必须承认,我不能 100% 确定我完全理解它的工作原理。但它完全按照我希望的方式工作。谢谢!
    • +1,教会了我一些我不知道的东西。这是一段非常好的代码。
    • 是否可以只为非字符串设置conversion_proxy,这样我就可以创建另一个显式返回字符串的同名函数?
    【解决方案2】:

    我无法从您的问题中判断您是否知道这一点,但您确实可以使用模板来做到这一点。唯一的问题是,您必须在每次调用中指定要转换的类型,而不是依赖推理(因为正如您所说,参数类型将始终相同)。

    template<typename T> T parse(const string& str) { /* do stuff for other types */ }
    template<> int parse<int>(const string& str) { /* do stuff for ints */ }
    template<> double parse<double>(const string& str) { /* do stuff for doubles */ }
    template<> bool parse<bool>(const string& str) { /* do stuff for bools */ }
    // etc.
    

    然后调用为

    int value = parse<int>("37");
    double value = parse<double>("3.14");
    bool value = parse<bool>("true");
    

    如果您已经知道这一点,请忽略此答案,但从您的问题中不清楚您是否知道这是可能的。

    当然,如果您所做的不是真正的通用(因此您必须专门针对要解析的每种类型),那么编写模板也不是正确的做法。

    顺便说一句,您可以使用这样的单个函数非常通用地完成它(假设 parse 是您真正想要做的):

    #include <sstream>
    template<typename T> T parse(const string& str) 
    {
      T t;
      std::istringstream sstr(str);
      sstr >> t;
      return t;
    }
    

    这适用于任何默认可构造、可流提取的类型,包括所有内置函数。

    【讨论】:

    • 这并不比拥有“int parse_int(const string&)”、“double parse_double(const string&)”等好。
    • 这就是为什么我展示了它可以用模板完成,然后指出如果它实际上不是通用的,你不想使用模板,然后展示了它如何是通用的(对于许多类型)。
    • 感谢您的回复。我没想到你能做到这一点。这似乎与仅仅制作 3 个单独的功能并没有太大的不同,但它仍然是一个方便的信息。我最初尝试实现此方法,但我无法弄清楚。
    • +1。尽管可以避免通过用户定义的转换函数以任何方式指定返回类型(请参阅 litb 的答案),但您的解决方案具有明确的可读性优势。您的 parse() 函数本质上是 Boost 的 lexical_cast() :)
    【解决方案3】:

    您可以将输出参数作为指针或引用传递。

    像这样:

    template<class T> void parse(const std::string &input, T& output);
    

    然后代码如下:

    double d; parse(input, d);
    int i; parse(input, i);
    

    应该可以。

    但是,您的代码看起来非常适合 std::istringstream,它只是:

    istringstream is(input);
    input >> d;
    

    如果你有一些复杂的格式,我很幸运的一个技巧是使用自定义运算符创建自定义对象>>,它会提取数据。

    那么它可能是这样的:

    istringstring is(input);
    input >> LineExtracter(x, y, d);
    

    【讨论】:

      【解决方案4】:

      我会同意比我快一点的 litb。使用强制转换运算符。

      #include <iostream>
      #include <string>
      #include <sstream>
      
      class Convertible
      {
      public:
          int m_Integer;
          bool m_Bool;
          double m_Double;
      
          Convertible() : m_Integer(0), m_Bool(false), m_Double(0.0) {};
      
          operator int() const
          {
              return m_Integer;
          }
          operator bool() const
          {
              return m_Bool;
          }
          operator double() const
          {
              return m_Double;
          }
      };
      
      Convertible parse(std::string data)
      {
          Convertible l_result;
      
          std::istringstream converter(data);
          converter >> l_result.m_Integer;
      
          std::istringstream converter2(data);
          converter2 >> l_result.m_Bool;
      
          std::istringstream converter3(data);
          converter3 >> l_result.m_Double;
      
          return l_result;
      }
      
      void main()
      {
          int l_convertedInt = parse("2");
          bool l_convertedBool = parse("true");
          double l_convertedDouble = parse("3.14");
      
          std::cout << "Converted '2' to " << l_convertedInt << std::endl;
          std::cout << "Converted 'true' to " << l_convertedBool << std::endl;
          std::cout << "Converted '3.14' to " << l_convertedDouble << std::endl;
      }
      

      【讨论】:

        【解决方案5】:

        很遗憾,这是不可能的。在 C++ 中,不可能根据函数的返回值重载函数。您要么必须拥有 3 个函数,ParseInt、ParseFloat 和 ParseBool,要么使用函数模板。

        【讨论】:

        • 技术上是正确的,但您可以通过用户定义的转换实现很多相同的事情——请参阅 litb 的出色答案。
        【解决方案6】:

        您可以返回 void*,然后根据需要转换结果。

        我建议不要这样做。 C++ 是一种强类型语言。这样做的好处是编译器可以比动态类型语言更早地捕获错误。

        【讨论】:

        • 实际上两者都行不通。返回 void* 的代码不知道 void* 将被转换为 float* 还是 int*。 Boost::variant 更安全,因为它会在定义明确的问题上失败,但它会遇到同样的水晶球问题。
        【解决方案7】:

        不,这种行为在 C++ 中是不可能的。为了允许,它必须能够在相同的范围内定义相同名称的函数,这些函数仅在返回类型上有所不同。这在 C++ 中是不合法的。

        C++ 可以做一些返回类型的特殊化,例如覆盖虚函数的协变返回类型。但它不支持您要查找的内容。

        【讨论】:

        • 从技术上讲,确实不能通过 C++ 中的返回类型重载,但您可以通过用户定义的转换来实现很多相同的事情——请参阅 litb 的出色答案。
        【解决方案8】:

        这是我对@Tyler McHenry's answer 的改编,适用于parse() 的参数不是字符串的类型。

        请注意,我发现我必须引入 模板专业化 以避免类型转换警告floatint)。

        (另见live demo。)

        #include <iostream>
        
        struct MyUnion
        {
        public:
          union {
            bool bool_value;
            int int_value;
            float float_value;
          };
        };
        
        template<typename T> T parse(const MyUnion& h)
        {
          T t;
        
          if (typeid(T) == typeid(bool)) {
            t = h.bool_value;
          } else if (typeid(T) == typeid(int)) {
            t = h.int_value;
          } else if (typeid(T) == typeid(float)) {
            // t = h.float_value; // see **Warning** below; use float specialization instead
          }
        
          return t;
        }
        
        // 'float' template specialization to avoid conversion warning.
        template<> float parse(const MyUnion& h)
        {
          return h.float_value;
        }
        
        int main()
        {
          MyUnion mu1; mu1.bool_value = true;
          MyUnion mu2; mu2.int_value = 42;
          MyUnion mu3; mu3.float_value = 3.14159;
        
          std::cout << "As bool: "  << parse<bool>(mu1)  << std::endl;
          std::cout << "As int: "   << parse<int>(mu2)   << std::endl;
          std::cout << "As float: " << parse<float>(mu3) << std::endl;
        }
        
        // **Warning**
        // In function 'T parse(const Heterogeneous&) [with T = int]':
        // Line 22: warning: converting to 'int' from 'const float'
        

        【讨论】:

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