【问题标题】:C++ same function parameters with different return typeC ++相同的函数参数具有不同的返回类型
【发布时间】:2013-02-12 19:24:10
【问题描述】:

我需要找到一些方法来模拟 C++ 中函数返回类型的重载。

我知道没有办法直接做到这一点,但我希望有一些开箱即用的方法。 我们正在创建一个供用户使用的 API,他们将传入一个数据字符串,该字符串根据字符串信息检索值。这些值是不同的类型。本质上,我们想让他们这样做:

int = RetrieveValue(dataString1);
double = RetrieveValue(dataString2);
// Obviously, since they don't know the type, they wouldn't use int =.... It would be:
AnotherFunction(RetrieveValue(dataString1)); // param of type int
AnotherFunction(RetrieveValue(dataString2)); // param of type double

但这在 C++ 中不起作用(显然)。 现在,我们正在设置它以便他们调用:

int = RetrieveValueInt(dataString1);
double = RetrieveValueDouble(dataString2);

但是,我们不希望他们知道他们的数据字符串的类型是什么。

很遗憾,我们不允许使用外部库,所以不要使用 Boost。

有什么办法可以解决这个问题吗?

澄清一下,我知道 C++ 本身无法做到这一点。但必须有某种方法可以绕过它。比如我想过做RetrieveValue(dataString1, GetType(dataString1))。这并不能真正解决任何问题,因为 GetType 也只能有一种返回类型。但我需要这样的东西。

我知道以前有人问过这个问题,但意义不同。我不能使用任何明显的答案。我需要一些完全开箱即用的东西才能对我有用,而其他问题中的任何答案都不是这种情况。

【问题讨论】:

  • 他们如何知道将函数的结果分配给什么类型?
  • 您正试图走一条错误的道路。不同的类型是不同的类型,不同的功能是不同的功能。这种语法重载不会增加任何价值。相反,它会给你的代码增加不必要的复杂性。不要这样做。
  • 您会展示您希望调用此函数的实际方式吗?而 int x = RetrieveValue(...) 是不是因为他们必须为 x 指定 int ,这会破坏您的条件之一。我认为这个问题构思不当。
  • 是的,我使用 int = RetrieveValue(...) 作为一个简单的例子。他们实际上将在另一个函数内部调用该函数。所以它可能是:AnotherFunction(RetrieveValue(...),param);

标签: c++ function types overloading


【解决方案1】:

你必须从这个开始:

template<typename T>
T RetrieveValue(std::string key)
{
     //get value and convert into T and return it
}

为了支持这个功能,你需要做更多的工作,以便将值转换为T 类型。转换价值的一种简单方法是:

template<typename T>
T RetrieveValue(std::string key)
{
     //get value
      std::string value = get_value(key, etc);

      std::stringstream ss(value);
      T convertedValue;
      if ( ss >> convertedValue ) return convertedValue;
      else throw std::runtime_error("conversion failed");
}

请注意,您仍然必须将此函数称为:

int x = RetrieveValue<int>(key);

如果您可以这样做,您可以避免提及int 两次:

Value RetrieveValue(std::string key)
{
     //get value
      std::string value = get_value(key, etc);
      return { value };
}

Value 的实现方式为:

struct Value
{
    std::string _value;

    template<typename T>
    operator T() const   //implicitly convert into T
    {
       std::stringstream ss(_value);
       T convertedValue;
       if ( ss >> convertedValue ) return convertedValue;
       else throw std::runtime_error("conversion failed");
    }
}

那么你可以这样写:

int    x = RetrieveValue(key1);
double y = RetrieveValue(key2);

你想要哪个,对吧?

【讨论】:

  • 作为 OP 的提示,搜索“模板专业化”。
  • 但是,在这种情况下,他们仍然必须指定类型。无法推断。因此,他们将不得不写RetrieveValue&lt;int&gt;,而不是RetrieveValueInt,我认为那里没有太大区别,除了后者可以通用。
  • @Benjamin 他们无论如何都在指定类型,肯定是int x =
  • @JonBentley:显然,这不是 OP 希望它被使用的方式。查看他的最新评论。他肯定选错了语言。
  • @Benjamin 是的,刚刚看到。我建议他编辑他的问题。
【解决方案2】:

无论是重载还是特化,您都需要将信息包含在函数签名中。您可以将变量作为未使用的第二个参数传递:

int RetrieveValue(const std::string& s, const int&) {
  return atoi(s.c_str());
}
double RetrieveValue(const std::string& s, const double&) {
  return atof(s.c_str());
}

int i = RetrieveValue(dataString1, i);
double d = RetrieveValue(dataString2, d);

【讨论】:

  • 我发现自己使用这种完全相同的模式在我的 GUI 小部件创建函数中“重载”返回类型。
【解决方案3】:

唯一明智的做法是将返回值移动到参数中。

 void retrieve_value(std::string s, double& p);
 void retrieve_value(std::string s, int& p);
 <...>

 double x;
 retrieve_value(data_string1, x);

 int y;
 retrieve_value(data_string2, y);

【讨论】:

  • 您声明参数的顺序与实际参数的顺序不匹配。
  • 这种方法的缺点是,如果使用对象,它们必须已经被构造,这可能需要实现虚拟构造函数
【解决方案4】:

如果你知道你的值永远不会是零或负值,只需返回一个包含 int 的结构,然后将不需要的值加倍并归零......

这是一种廉价而肮脏但简单的方法......

struct MyStruct{
int myInt;
double myDouble;
};

MyStruct MyFunction(){
}

【讨论】:

  • 或者添加一个布尔值或第三个值,指示应该读取哪个值(int 或 double)。同样,这不是最佳的或优雅的,但超级直接......一个类也可以工作,但如果你只有 int 和 double 就没有真正意义。
  • 我们已经考虑过这一点,但是,我们不能让用户必须处理一个结构。
  • 您有两个选择:使用类或结构,或者通过引用传递参数,这意味着没有返回值(或者至少不是 int 和 double)。有一种可怕的方法可以做到这一点:您可以返回一个包含值和类型的字符串,因此您将拥有“int 1234”或“double 123.456”,但随后用户必须破译......对不起,但你没有很多选择。
【解决方案5】:

如果数据字符串是编译时常量(如在回答我的评论时所说),您可以使用一些模板魔法来完成这项工作。一个更简单的选择是根本不使用字符串,而是使用一些允许您重载参数的数据类型。

struct retrieve_int {} as_int;
struct retrieve_double {} as_double;

int RetrieveValue(retrieve_int) { return 3; }
double RetrieveValue(retrieve_double) { return 7.0; }

auto x = RetrieveValue(as_int);    // x is int
auto y = RetrieveValue(as_double); // y is double

【讨论】:

    【解决方案6】:

    不幸的是没有办法重载函数返回类型见这个答案 Overloading by return type

    【讨论】:

    • 我会说“幸运的是,...”。
    • +1 对此,“幸运”是常识,但不幸的是,如果出于某种原因您真的不想要该功能。
    • 这实际上是 Ada 中一个非常有用的功能,但它只能在那里工作,因为 Ada 缺乏隐式转换。
    【解决方案7】:
    int a=itoa(retrieveValue(dataString));
    double a=ftoa(retrieveValue(dataString));
    

    两者都返回一个字符串。

    【讨论】:

      【解决方案8】:

      作为模板解决方案的替代方案,您可以让函数返回一个引用或指向某个类的指针,然后创建该类的子类以包含您想要返回的不同数据类型。 RetrieveValue 然后将返回对适当子类的引用。

      这会让用户在不知道它属于哪个子类的情况下将返回的对象传递给其他函数。

      这种情况下的问题将变成内存管理问题——选择哪个函数分配返回的对象,哪个函数删除它,以及何时删除它,以避免内存泄漏。

      【讨论】:

        【解决方案9】:

        答案很简单,只需声明返回 void* 类型的函数,并在定义中返回对不同类型变量的引用。例如在标头 (.h) 中声明

        void* RetrieveValue(string dataString1);
        

        并且在定义(.cpp)中只写

        void* RetrieveValue(string dataString1)
        {
            if(dataString1.size()<9)
            {
                static double value1=(double)dataString1.size();
                return &value1;
            }
            else
            {
                static string value2=dataString1+"some string";
                return &value2;
            }
        }
        

        然后在调用 RetrieveValue 的代码中只需转换为正确的值

        string str;
        string str_value;
        double dbl_value;
        if(is_string)
        {
            str_value=*static_cast<*string>(RetrieveValue(str));
        }
        else
        {
            dbl_value=*static_cast<*double>(RetrieveValue(str));
        }
        

        【讨论】:

        • 我忘了提到返回的变量必须是静态的,以便在你制作一个库时可以从函数翻译单元外部访问这些值。
        【解决方案10】:

        由于您使用的示例并非您真正想要的,因此您有点让所有人失望。

        您真正拥有的设置(调用返回类型未知的函数的返回值的函数)将不起作用,因为函数调用是在编译时解析的。

        然后,您将受限于运行时解决方案。我推荐访问者模式,你必须大幅改变你的设计以允许这种改变。我真的没有其他方法可以做到这一点。

        【讨论】:

        • 我的例子怎么不是我想要的?
        • @JoshJohnson 你说你问题中的例子不是它的真正用途,不是吗?
        • 它将被使用,如图所示。它可以在另一个函数调用中更直接地使用,但是这不会清楚地显示所需的区别。我给出的示例仍然是该功能的有效用途。你说这“不是我真正想要的”。这不是真的。我确实想要那个...
        • @JoshJohnson 但这并不是你想要的。在解决如何处理返回类型时,示例 int x = f(str)f(g(str)) 甚至不一样。正如我所说,除了我在这个答案中写的内容之外,没有解决您的问题的方法。否决票不会改变这一事实。
        • 如果我只给出第二个例子,人们怎么会明确知道不同的返回类型呢?那将如何改变解决方案?在这两种情况下,都需要各种返回值。究竟发生了什么变化?
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-04-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-12-18
        • 1970-01-01
        • 2015-04-18
        相关资源
        最近更新 更多