11
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:我正在尝试找到一种替代方法来返回包含这些数据类型的结构/联合。

4

14 回答 14

29

你可以使用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而不是 aunion因为你不能在里面使用非 POD 类型union。此外,boost::any如果您不知道要处理的类型,那也很好。否则,我会使用它,boost::variant因为它更高效、更安全。


回答已编辑的问题:如果您不想Boost随代码一起发布,请查看bcp. bcp来自同一链接的描述:

bcp 实用程序是一种用于提取 Boost 子集的工具,它对于希望将其库与 Boost 分开分发的 Boost 作者以及希望将 Boost 子集与他们的应用程序一起分发的 Boost 用户很有用。

bcp 还可以报告您的代码依赖于 Boost 的哪些部分,以及这些依赖项使用了哪些许可证。

于 2009-08-31T17:25:16.840 回答
8

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

于 2009-08-31T17:26:32.703 回答
6

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

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;
}
于 2009-08-31T19:14:40.300 回答
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);
    }
}
于 2009-08-31T17:27:11.267 回答
3

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

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

请记住,void*应该指向驻留在堆上的值(即使用newor动态分配malloc)并且调用者应该注意释放分配的对象。

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

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

于 2009-08-31T17:35:10.657 回答
3

实现这样的事情的常用方法是 C,它并不总是在 C++ 中工作,是使用联合和类型字段:

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++ 中的任何值类型提供这种构造。

于 2009-08-31T17:36:46.147 回答
3

编辑: boost::any using bcp (感谢 AraK)似乎是迄今为止最好的解决方案,但是否有可能(在某种程度上)证明不存在针对这个问题的 ANSI C++ 解决方案?

您似乎对这里的术语有些困惑。

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

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

我无法想象你会寻找什么样的“证据”。C++ 是散文形式的文档。它不是一个数学方程式。它不能被“证明”,除非说“去阅读标准”。证明某些东西在语言或标准库中定义的很容易——只需指出它在标准中的描述位置。但是证明存在某些东西基本上是不可能的——除了列举标准的一个句子,并记录它们都没有描述你正在寻找的东西。我怀疑你会找到愿意为你做件事的人。

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

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

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

而且我敢打赌,您自制的解决方案不会是 ANSI C++。它将依赖于某种形式的未定义行为。如果你想要一个 ANSI-C++ 解决方案,你最好的选择是 Boost。

于 2009-08-31T18:46:30.110 回答
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;
}
于 2009-08-31T17:33:39.397 回答
0

Adobe Source Libraries 也有adobe::any_regular_t,它允许您存储任何类型,只要它模拟了Regular概念。您将包装返回值的方式与使用boost::any. (链接页面上还有文档说明两者有何adobe::any_regular_t不同boost::any——当然,您选择的类型应取决于代码的要求。)

于 2009-08-31T17:37:28.873 回答
0

您可以通过引用传递并进行类型保存并检查它是否同时工作,也不会涉及任何其他库(您的 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;
}

...

我发现这个解决方案在设计方面通常更干净。不幸的是,函数签名不允许多种类型作为返回类型,但是这样你可以传递任何东西。

于 2009-08-31T18:14:51.373 回答
0

从 C++17 开始,有std::anyand std::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;
}
于 2020-12-13T01:10:40.893 回答
-1

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

于 2009-08-31T17:24:42.630 回答
-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 函数中,您可以放置​​更多代码并让它们调用其他函数以不重复代码。

于 2009-08-31T18:28:43.967 回答
-3

某事=无效*

您必须转换返回值,因此您必须知道返回什么。

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;
        }
    }
于 2009-08-31T17:36:45.070 回答