Функция, возвращающая неизвестный тип

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 () может возвращать переменную переменного типа данных?

по существу, с чем я заменяю что-то ?

PS: Я пытаюсь найти альтернативу возвращению структуры / объединения, содержащего эти типы данных.

13 ответов


можно использовать boost::any или 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 потому что вы не можете использовать типы non-POD внутри union. Также, boost::any отлично, если вы не знаете, с каким типом вы имеете дело. В противном случае я бы использовал boost::variant потому что это гораздо эффективнее и безопаснее.


ответ на отредактированный вопрос: если вы не хотите грузить Boost С вашим кодом, взгляните на bcp. Описание bcp из той же ссылки:

утилита bcp является инструментом для извлечение подмножеств Boost, это полезно для авторов Boost, которые хотят распространять их библиотека отдельно от Boost и для пользователей Boost, которые хотите распределить подмножество Boost с их применением.

bcp также может сообщить о том, какие части Boost ваш код зависит от, и какие лицензии используются зависимости.


C++ является строго типизированным языком и не имеет понятия неизвестного типа. Вы можете попробовать использовать boost:: any, который может (вроде) указать любой тип. Однако я бы поставил под сомнение дизайн вашей функции.


Если вы знаете тип во время компиляции, вы можете использовать шаблоны. Если тип зависит от времени выполнения, то использование шаблонов не является опцией.

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

использовать boost:: любой:

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

обычным способом достижения чего-то подобного является 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++, когда один из возможных типов значений является классом с нетривиальным конструктором, потому что не всегда будет ясно, какой конструктор следует вызывать. импульс.Вариант использует более сложную версию этого подхода для обеспечения такого типа конструкции для любых типов значений в C++.


вы можете использовать структуру, содержащую void* указывая на значение, которое вы хотите получить вместе с size_t это указывает на размер возвращаемого объекта. Что-то вроде этого:--7-->

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

помните, что void* должно указывать на значение, находящееся в куче (т. е. динамически выделяемое с помощью new или malloc) и вызывающий должен позаботиться об освобождении выделенного объекта.

сказав это, я думаю, что это плохая идея в общем и целом.

Edit: вы также можете рассмотреть вопрос о включении флага, указывающего, что было возвращено в вышеуказанной структуре, чтобы вызывающий абонент мог понять это, если вызывающий абонент не знает, какой тип ожидать.


EDIT: boost:: любое использование bcp (спасибо AraK) кажется лучшим решением на сегодняшний день, но можно ли доказать (в какой-то степени), что нет решения ANSI C++ для этой проблемы?

Вы кажется немного запутались в терминологии.

во-первых, давайте назовем это ISO C++, не так ли? Он был стандартизирован ISO в 1998 году, и с тех пор именно это люди упоминали, когда говорили о "стандартном c++". Теперь, что вы подразумеваете под " ANSI C++ решение"?

  • решение, которое компилируется чисто, используя только ANSI (или ISO) C++? Если это так, Boost is решение ANSI C++
  • решение уже реализовано в стандартной библиотеке ANSI C++? Если это так, то нет, такого решения не существует (и нет никакого "доказательства", кроме " прочитайте стандарт языка и посмотрите, сможете ли вы найти такой класс. Если вы не можете, его там нет".
  • решение можно реализовать сами используя только ANSI C++. Тогда ответ: "да, вы можете скопировать исходный код из Boost".

Я не могу себе представить, какое "доказательство" вы бы искали. C++ - это документ в форме прозы. Это не математическое уравнение. Это не может быть "доказано", кроме как сказав:"идите читать стандарт". Доказывая, что что-то is определенный в языке или в стандартной библиотеке легок -- просто укажите вне где в стандарте оно описанный. Но доказать это что-то не в принципе невозможно-кроме как перечисляя одно предложение стандарта и документ, который ни один из них не описывает то, что вы ищете. И я сомневаюсь, что вы найдете кого-то, кто захочет это сделать это для вас.

в любом случае, правильное стандартное решение на C++ is использовать Boost. Это не тяжеловесное решение. Boost довольно легкий в том, что вы можете включить ровно биты, которые вам нужны, без каких-либо зависимостей от остальной части коллекции библиотеки.

из того, что вы описали (легкое приложение для широкой пользовательской базы), есть нулевая причина не использовать Boost. Это может упростить ваш код и уменьшить количество ошибок, вызванных попыткой изобрести колесо. При распространении скомпилированного исполняемого файла он имеет нулевую стоимость. The Boost.Any библиотека, как и большая часть Boost, только для заголовков и просто скомпилирована в исполняемый файл. Отдельные библиотеки не должны распространяться.

нет ничего, что можно получить, пытаясь изобрести колесо. Ваш исполняемый файл будет не меньше и не эффективнее, но он будет быть более глючные.

и я готов поспорить, что ваше домашнее решение будет не быть ANSI C++. Она будет полагаться на неопределенное поведение. Если вы хотите решение ANSI-C++, ваш лучший выбор-Boost.


вы можете использовать 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;
}

исходные библиотеки Adobe также имеют adobe::any_regular_t, что позволяет хранить любой тип, пока он моделирует обычный концепции. Вы бы обернуть возвращаемое значение так же, как вы бы с boost::any. (На связанной странице также есть документация о том, как adobe::any_regular_t отличается от boost::any -- конечно тип вы выбираете, должен зависеть от требований вашего кода.)


вы могли бы пройти по ссылке вместо этого и быть 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 не позволяют использовать несколько типов в качестве возвращаемых типов, но таким образом вы можете передать что угодно.


Если пользователь знает, что положить в, вы можете использовать шаблон, чтобы исправить это. Если нет, я не могу придумать никакого решения.


Я думаю, что проблема в этой конструкции функции. Вы пробовали перегружать?

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 вы можете разместить больше кода и заставить их вызывать другие функции, чтобы не повторять код.


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