返回未知类型的函数

时间:2009-08-31 17:22:14

标签: c++ visual-c++-2005 variant

class Test
{
public:

 SOMETHING DoIt(int a)
 {
  float FLOAT = 1.2;
  int INT = 2;
  char CHAR = 'a';

  switch(a)
  {
  case 1: return INT;
  case 2: return FLOAT;
  case 3: return CHAR;
  }
 }
};


int main(int argc, char* argv[])
{  
 Test obj;
 cout<<obj.DoIt(1);    
    return 0;
}

现在,使用a = 1意味着我需要返回一个整数等的知识,无论如何Doit()可以返回一个变量数据类型的变量吗?

基本上,我将如何替换 SOMETHING

PS:我正在尝试寻找一种替代方法来返回包含这些数据类型的结构/联合。

14 个答案:

答案 0 :(得分:25)

您可以使用boost::anyboost::variant来执行您想要的操作。我推荐boost::variant,因为您知道要返回的类型集合。


这是一个非常简单的示例,但您可以使用variant执行更多操作。查看参考资料以获取更多示例:)

#include "boost/variant.hpp"
#include <iostream>

typedef boost::variant<char, int, double> myvariant;

myvariant fun(int value)
{
 if(value == 0)
 {
  return 1001;
 }
 else if(value  == 1)
 {
  return 3.2;
 }
  return 'V';
}

int main()
{
 myvariant v = fun(0);
 std::cout << v << std::endl;

 v = fun(1);
 std::cout << v << std::endl;

 v = fun(54151);
 std::cout << v << std::endl;
}

输出:

1001
3.2
V

我会使用boost::variant而不是union,因为您无法在union内使用非POD类型。另外,如果你不知道你正在处理的类型,boost::any是很好的。否则,我会使用boost::variant,因为它更有效,更安全。


回答已修改的问题:如果您不想使用代码发送Boost,请查看bcp。来自同一链接的bcp说明:

  

bcp实用程序是一个工具   提取Boost的子集,就是这样   对于想要的Boost作者很有用   分别分发他们的库   来自Boost,以及Boost用户   想要分发Boost的子集   与他们的应用。

     

bcp还可以报告哪些部分   提升您的代码依赖于,和   那些使用的许可证   的依赖关系。

答案 1 :(得分:8)

C ++是一种强类型语言,没有未知类型的概念。你可以尝试使用boost :: any,它可以(有点)指定任何类型。不过,我会质疑你的功能设计。

答案 2 :(得分:5)

如果您在编译时知道类型,则可以使用模板。如果type取决于运行时,那么使用模板不是一种选择。

class Test
{
  template<int> struct Int2Type {};
  template<>    struct Int2Type<1> { typedef int value_type; };
  template<>    struct Int2Type<2> { typedef float value_type; };
  template<>    struct Int2Type<3> { typedef char value_type; };

public:
  template<int x> typename Int2Type<x>::value_type DoIt() {}; // error if unknown type used
  template<> typename Int2Type<1>::value_type DoIt<1>() { return 2; };
  template<> typename Int2Type<2>::value_type DoIt<2>() { return 1.2f; };
  template<> typename Int2Type<3>::value_type DoIt<3>() { return 'a'; };
};

int main()
{
  Test obj;
  cout << obj.DoIt<2>(); 
  return 0;
}

答案 3 :(得分:3)

使用boost::any

boost::any DoIt(int a)
{
    float FLOAT = 1.2;
    int INT = 2;
    char CHAR = 'a';

    switch(a)
    {
    case 1: return boost::any(INT);
    case 2: return boost::any( FLOAT);
    case 3: return boost::any( CHAR);
    }
}

答案 4 :(得分:3)

您可以使用包含void*的结构,指向要返回的值以及指示要返回的对象大小的size_t。像这样:

struct Something {
    void *value;
    size_t size;
};

请记住,void*应该指向驻留在堆上的值(即使用newmalloc动态分配),并且调用者应该负责释放已分配的对象。 / p>

话虽如此,我认为这总体上是一个坏主意。

编辑:您可能还需要考虑在上述结构中包含一个标志,指示返回的内容,以便调用者可以理解它,除非调用者知道期望的类型。

答案 5 :(得分:3)

实现类似这样的东西的常用方法是C,它在C ++中并不总是有效,是一个union和一个类型字段:

enum SomeType { INT, FLOAT, CHAR };
struct Something
{
    SomeType type;
    union
    {
        int i;
        float f;
        char c;
    };
};

Something DoIt(int a)
{
    Something s;
    switch (a)
    {
      case 1:
        s.type = INT;
        s.i = 2;
        break;
      case 2:
        s.type = FLOAT;
        s.f = 1.2;
        break;
      case 3:
        s.type = CHAR;
        s.c = 'a';
        break;
      default:
        // ???
    }
    return s;
}

当一个可能的值类型是一个具有非平凡构造函数的类时,这在C ++中不起作用,因为它并不总是清楚应该调用哪个构造函数。 Boost.Variant使用此方法的更复杂版本为C ++中的任何值类型提供此类构造。

答案 6 :(得分:3)

  编辑:boost ::任何使用bcp(感谢AraK)似乎是迄今为止最好的解决方案,但有可能证明(在某种程度上)没有ANSI C ++解决方案来解决这个问题吗?

你似乎对这里的术语感到有些困惑。

首先,我们称之为ISO C ++,不是吗?它在1998年由ISO标准化,从那时起,人们在谈论“标准C ++”时就提到了这一点。 现在,“ANSI C ++解决方案”是什么意思?

  • 仅使用ANSI(或ISO)C ++进行干净编译的解决方案?如果是这样,Boost 是ANSI C ++解决方案
  • ANSI C ++标准库中的解决方案已经实现?如果是,那么不,没有这样的解决方案(并且没有“证明”,除了“通过语言标准阅读,看看你是否能找到这样的课程。如果你不能,那就不存在”。
  • 您只能使用ANSI C ++实现自己的解决方案。然后答案是“是的,你可以从Boost复制源代码”。

我无法想象你会寻找什么样的“证明”。 C ++是散文形式的文档。它不是一个数学方程式。它不能被“证明”,除非说“去读标准”。证明在语言或标准库中定义 的内容很简单 - 只需指出标准中描述的位置即可。但证明不是的东西基本上是不可能的 - 除了枚举标准的每个单个句子,并记录它们都没有描述你要找的东西。我怀疑你会发现有人愿意为你做

无论如何,使用Boost正确的标准C ++解决方案 。 它不是一个重量级的解决方案。 Boost非常轻量级,因为您可以将完全包含在您需要的位中,而不依赖于库集合的其余部分。

根据您的描述(广泛用户群的轻量级应用程序),没有理由不使用Boost。它可以简化您的代码并减少因重新发明轮子而导致的错误数量。分发已编译的可执行文件时,它的成本为零。 Boost.Any库与Boost一样,仅限于标头,并且只是编译到您的可执行文件中。不需要分发单独的库。

试图重新发明轮子没有任何好处。您的可执行文件将不会更小或更高效,但它更多的错误。

而且我愿意打赌你自己酿造的解决方案是ANSI C ++。它将依赖某种形式的未定义行为。如果你想要ANSI-C ++解决方案,最好的选择是Boost。

答案 7 :(得分:1)

你可以使用联盟:

typedef union {
  int i;
  float f;
  char c;
} retType;

retType DoIt(int a){
  retType ret;

  float FLOAT = 1.2;
  int INT = 2;
  char CHAR = 'a';

  switch(a)
  {
    case 1: ret.i = INT; break;
    case 2: ret.f = FLOAT; break;
    case 3: ret.c = CHAR; break;
  }
  return ret;
}

答案 8 :(得分:0)

Adob​​e源库也有adobe::any_regular_t,只要它为Regular概念建模,就可以存储任何类型。您可以像使用boost::any一样包装返回值。 (链接页面上还有关于adobe::any_regular_tboost::any的区别的文档 - 当然,您选择的类型应取决于代码的要求。)

答案 9 :(得分:0)

您可以通过引用传递而不是typesave并检查它是否同时工作,也不会涉及任何其他库(您的ansi C ++解决方案):

bool DoIt (int i, int & r1)
{
  if (i==1) {r1 = 5; return true}
  return false;
}

bool DoIt (int i, double & r2)
{
  if (i==2) {r2 = 1.2; return true}
  return false;
}

...

我发现这种解决方案在设计方面往往更加清洁。不幸的是,funciton签名不允许多种类型作为返回类型,但这样你就可以传递任何东西。

答案 10 :(得分:0)

从C ++ 17开始,有std::anystd::variant,这意味着您不需要第三方库。从@Arak的答案中,将对代码进行如下修改。

#include <variant>
#include <any>
#include <iostream>

typedef std::variant<char, int, double> myvariant;

myvariant fun(int value)
{
   if(value == 0)
   {
      return 1001;
   }
   else if(value  == 1)
   {
      return 3.2;
   }
   return 'V';
}

int main()
{
   myvariant v = fun(0);
   std::cout << v << std::endl;

   v = fun(1);
   std::cout << v << std::endl;

   v = fun(54151);
   std::cout << v << std::endl;
}

答案 11 :(得分:-1)

如果用户知道放入了什么,您可以使用模板来解决此问题。如果没有,我想不出任何解决方案。

答案 12 :(得分:-1)

我认为问题在于这个功能设计。你试过重载吗?

class Test
{

public:

int DoIt(int a) {

  int INT = 2;
   return INT;

} 

float DoIt(float a) {

float FLOAT = 1.2; 
return FLOAT;

} 

char DoIt(char a) {

char CHAR = 'a'; 
return CHAR;

} 

};


int main(int argc, char* argv[])
{       
    Test obj;

//....

switch(a)
case 1: 
    cout<< obj.DoIt(1);    
break;

case 2:
cout<< obj.DoIt(1.01);   
break;

case 3:
cout<< obj.DoIt("1");   
break;

    return 0;
}

在DoIt函数内部,您可以放置​​更多代码,并让它们调用其他函数,以避免重复代码。

答案 13 :(得分:-3)

SOMETHING = void *

您必须转换返回的值,因此您必须知道返回的内容。

void* DoIt(int a)
    {
        float FLOAT = 1.2;
        int INT = 2;
        char CHAR = 'a';

        switch(a)
        {
        case 1: return &INT;
        case 2: return &FLOAT;
        case 3: return &CHAR;
        }
    }