返回未知类型的函数

发布于 2024-08-03 15:41:49 字数 488 浏览 8 评论 0原文

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

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;
}

Now, using the knowledge that a = 1 implies that I need to return an integer, etc., is there anyway Doit() can return a variable of variable data type?

Essentially, with what do I replace SOMETHING ?

PS: I'm trying to find a an alternative to returning a structure/union containing these data types.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(14

月光色 2024-08-10 15:41:50

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;
        }
    }

SOMETHING = void*

You have to cast the returned value, so you have to know what is returned.

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;
        }
    }
南风几经秋 2024-08-10 15:41:49

您可以使用 boost::any< /a> 或 boost::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 因为你不能在 中使用非 POD 类型联盟。另外,如果您不知道正在处理的类型,则 boost::any 非常有用。否则,我会使用 boost::variant 因为它更高效、更安全。


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

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

bcp 还可以报告哪些部分
Boost 你的代码依赖于,并且
这些人使用什么许可证
依赖关系。

You can use boost::any or boost::variant to do what you want. I recommend boost::variant because you know the collection of types you want to return.


This is a very simple example, though you can do much more with variant. Check the reference for more examples :)

#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;
}

The output:

1001
3.2
V

I would use boost::variant instead of a union because you can't use non-POD types inside union. Also, boost::any is great if you don't know the type you are dealing with. Otherwise, I would use boost::variant because it is much more efficient and safer.


Answering the edited question: If you don't want to ship Boost with your code, take a look at bcp. The description of bcp from the same link:

The bcp utility is a tool for
extracting subsets of Boost, it's
useful for Boost authors who want to
distribute their library separately
from Boost, and for Boost users who
want to distribute a subset of Boost
with their application.

bcp can also report on which parts of
Boost your code is dependent on, and
what licences are used by those
dependencies.

断念 2024-08-10 15:41:49

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

C++ is a strongly-typed language, and has no concept of an unknown type. You could try using boost::any, which can (sort of) specify any type. I would question the design of your function, however.

遗心遗梦遗幸福 2024-08-10 15:41:49

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

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;
}

If you know type at compile time you could use templates. If type depends on run-time, then using templates is not an option.

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;
}
も让我眼熟你 2024-08-10 15:41:49

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

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

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

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

我无法想象您会得到什么样的“证明” 。 C++ 是散文形式的文档,它不是一个数学方程,除非通过“去阅读标准”来证明。语言或标准库中的内容很容易 - 只需指出标准中的描述位置即可,但要证明不存在某些内容基本上是不可能的 - 除非枚举每个<。 /em> 标准的一句话,并记录它们都没有描述您正在寻找的内容,并且我怀疑您会找到愿意为您做的事情

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

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

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

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

EDIT: boost::any using bcp (thanks AraK) seems to be the best solution to date but is it possible to prove (to some extent) that there exists no ANSI C++ solution to this problem?

You seem a bit confused about the terminology here.

First, let's call it ISO C++, shall we? It was standardized by ISO in 1998, and since then, that is what people have referred to when talking about "standard C++".
Now, what do you mean by an "ANSI C++ solution"?

  • A solution that compiles cleanly using only ANSI (or ISO) C++? If so, Boost is the ANSI C++ solution
  • A solution already implemented in the ANSI C++ standard library? If so then no, no such solution exists (and there is no "proof", other than "go read through the language standard and see if you can find such a class. If you can't, it isn't there".
  • A solution you could implement yourself using only ANSI C++. Then the answer is "yes, you could go copy the source code from Boost".

I can't imagine what kind of "proof" you'd be looking for. C++ is a document in prose form. It is not a mathematical equation. It can not be "proven", except by saying "go read the standard". Proving that something is defined in the language or in the standard library is easy -- simply point out where in the standard it is described. But proving that something isn't there is basically impossible -- except by enumerating every single sentence of the standard, and document that none of them describe what you're looking for. And I doubt you'll find anyone willing to do that for you.

Anyway, the correct standard C++ solution is to use Boost.
It is not a heavy-weight solution. Boost is pretty lightweight in that you can include exactly the bits you need, with no dependencies on the rest of the library collection.

From what you've described (a light application for a broad user base), there is zero reason not to use Boost. It can simplify your code and reduce the number of bugs caused by attempting to reinvent the wheel. When distributing the compiled executable, it has zero cost. The Boost.Any library is, like much of Boost, header-only, and is simply compiled into your executable. No separate libraries have to be distributed.

There is nothing to be gained by trying to reinvent the wheel. Your executable will be no smaller or more efficient, but it will be more buggy.

And I'm willing to bet that your home-brewed solution will not be ANSI C++. It will rely on some form of undefined behavior. If you want an ANSI-C++ solution, your best bet is Boost.

在巴黎塔顶看东京樱花 2024-08-10 15:41:49

使用 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);
    }
}

Use 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);
    }
}
墟烟 2024-08-10 15:41:49

实现这样的事情的通常方法是 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++ 中的任何值类型提供这种构造的方法。

The usual way to achieve something like this is C, which doesn't always work in C++, is with a union and a type field:

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;
}

This doesn't work in C++ when one of the possible value types is a class with a non-trivial constructor, because it wouldn't always be clear which constructor should be called. Boost.Variant uses a more complex version of this approach to provide this kind of construct for any value types in C++.

a√萤火虫的光℡ 2024-08-10 15:41:49

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

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

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

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

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

You could use a struct containing a void* pointing to the value you want returned along with a size_t that indicates the size of the object being returned. Something like this:

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

Remember that the void* should point to a value residing on the heap (i.e. dynamically allocated using new or malloc) and the caller should take care of freeing the allocated object.

Having said that, I think it's a bad idea overall.

Edit: You may also want to consider including a flag indicating what was returned in the above structure so that the caller can make sense of it, unless the caller knows what type to expect.

述情 2024-08-10 15:41:49

你可以使用联合:

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;
}

You could use a union:

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;
}
骑趴 2024-08-10 15:41:49

Adobe Source Libraries 还具有 adobe::any_regular_t,它允许您可以存储任何类型,只要它符合 常规 概念即可。您可以像使用 boost::any 一样包装返回值。 (链接页面上还有关于 adobe::any_regular_tboost::any 有何不同的文档 - 当然,您选择的类型应该取决于你的代码。)

The Adobe Source Libraries also has adobe::any_regular_t, which allows you to store any type as long as it models the Regular concept. You would wrap your return value much the same way you would with boost::any. (There is also documentation on the linked page as to how adobe::any_regular_t differs from boost::any -- of course the type you pick should depend on the requirements of your code.)

心在旅行 2024-08-10 15:41:49

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

...

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

You could pass by reference instead and be typesave and check if it worked at the same time, would not involve any additional library either (your kind of ansi C++ solution):

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;
}

...

I find this solution often more clean in terms of design. It's unfortunate that funciton signatures don't allow multiple types as return types, but this way you can pass anything.

゛清羽墨安 2024-08-10 15:41:49

从 C++17 开始,有 std::any< /a> 和 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;
}

As of C++17 there is std::any and std::variant, which means you do not need third party library for this. From @Arak's answer the code will be modified a bit as below.

#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;
}
心的憧憬 2024-08-10 15:41:49

如果用户知道放入的内容,您可以使用模板来解决此问题。如果没有,我想不出任何解决办法。

If the user knows what is put in, you could use a template to fix this. If not, I can't think of any solution.

梦初启 2024-08-10 15:41:49

我认为问题出在这个功能设计上。你尝试过超载吗?

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 函数内部,您可以放置​​更多代码并让它们调用其他函数以避免重复代码。

I think the problem is about this function design. Have you tried overloading?

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;
}

Inside DoIt functions you can place more code and make them call other functions for not repeating code.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文