Question

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

Maintenant, en utilisant les connaissances que a = 1 implique que je dois retourner un entier, etc., est de toute façon Doït () peut retourner une variable de type de données variables?

Pour l'essentiel, avec ce que dois-je remplacer CHOSE ?

PS:. Je suis en train de trouver une alternative au retour d'une structure / union contenant ces types de données

Était-ce utile?

La solution

Vous pouvez utiliser boost::any ou boost::variant pour faire ce que vous voulez. Je recommande boost::variant parce que vous connaissez la collection de types que vous voulez revenir.


Ceci est un exemple très simple, mais vous pouvez faire beaucoup plus avec variant. Vérifiez la référence pour d'autres exemples:)

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

La sortie:

1001
3.2
V

J'utiliser boost::variant au lieu d'un union parce que vous ne pouvez pas utiliser les types non-POD à l'intérieur union. En outre, boost::any est très bien si vous ne connaissez pas le type que vous avez affaire. Dans le cas contraire, je l'utilise boost::variant car il est beaucoup plus efficace et plus sûr.


Répondre à la question éditée: Si vous ne voulez pas expédier Boost avec votre code, jetez un oeil à bcp . La description de bcp du même lien:

  

L'utilitaire bcp est un outil pour   extraire des sous-ensembles de Boost, il est   utile pour les auteurs de Boost qui veulent   distribuer leur bibliothèque séparément   de Boost, et pour les utilisateurs de Boost qui   vouloir distribuer un sous-ensemble de Boost   avec leur application.

     

bcp peut également faire rapport sur les parties de   Boostez votre code dépend, et   quelles licences sont utilisées par les   dépendances.

Autres conseils

C ++ est un langage fortement typé et n'a pas de concept d'un type inconnu. Vous pouvez essayer d'utiliser boost :: tout, qui peut (sorte de) spécifier tout type. Je doute la conception de votre fonction, cependant.

Si vous connaissez le type à la compilation, vous pouvez utiliser des modèles. Si le type dépend de l'exécution, puis à l'aide des modèles ne sont pas une 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;
}

Utilisez boost :: tout :

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

La manière habituelle pour obtenir quelque chose comme ceci est C, ce qui ne fonctionne pas toujours en C ++, est une union et un champ de type:

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

Cela ne fonctionne pas en C ++ lorsque l'un des types de valeurs possibles est une classe avec un constructeur non trivial, parce qu'il ne serait pas toujours clair que le constructeur devrait être appelé. utilise une version plus complexe de Boost.Variant cette approche pour fournir ce type de construction pour tous les types de valeurs en C ++.

Vous pouvez utiliser une struct contenant un void* montrant la valeur que vous souhaitez renvoyer avec un size_t qui indique la taille de l'objet retourné. Quelque chose comme ceci:

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

Rappelez-vous que le void* doit pointer sur une valeur se trouvant sur le segment (par exemple répartis de façon dynamique en utilisant new ou malloc) et l'appelant doit prendre soin de libérer l'objet alloué.

Cela dit, je pense qu'il est une mauvaise idée globale.

Modifier Vous pouvez également envisager d'inclure un drapeau indiquant ce qui a été renvoyé dans la structure ci-dessus pour que l'appelant peut lui donner un sens, à moins que l'appelant sait quel type d'attendre

  

EDIT: boost :: tout en utilisant bcp (merci Arak) semble être la meilleure solution à ce jour, mais est-il possible de prouver (dans une certaine mesure) qu'il n'y existe pas de solution ANSI C ++ à ce problème

?

Vous avez l'air un peu confus au sujet de la terminologie ici.

Tout d'abord, nous allons l'appeler ISO C ++, allons-nous? Il a été normalisé par l'ISO en 1998, et depuis lors, c'est-ce que les gens ont appelé quand on parle de « standard C ++ ». Maintenant, qu'est-ce que vous entendez par "ANSI C ++ solution"?

  • Une solution qui compile proprement utilisant seulement ANSI (ou ISO) C ++? Si oui, Boost la solution ANSI C ++
  • Une solution déjà mis en œuvre dans la bibliothèque standard ANSI C ++? Si oui, alors non, pas une telle solution existe (et il n'y a pas de « preuve », autre que « aller lire si vous ne pouvez pas, il est pas là par la norme linguistique et voir si vous pouvez trouver une telle classe. ».
  • Une solution que vous pourriez mettre en œuvre vous en utilisant uniquement ANSI C ++. Ensuite, la réponse est « oui, vous pouvez aller copier le code source de Boost ».

Je ne peux pas imaginer quel genre de « preuve » que vous souhaitez rechercher. C ++ est un document sous forme de prose. Il n'est pas une équation mathématique. Il ne peut pas être « prouvé », sauf en disant « allez lire la norme ». Prouver que quelque chose est défini dans la langue ou dans la bibliothèque standard est facile - il suffit de rappeler où dans la norme, il est décrit. Mais prouver que quelque chose est pas il est fondamentalement impossible - sauf en dénombrant tous phrase de la norme, et le document qu'aucun d'entre eux décrivent ce que vous cherchez . Et je doute que vous trouverez quelqu'un prêt à faire que pour vous.

Quoi qu'il en soit, la solution standard C ++ correcte est à utiliser Boost. Il n'est pas une solution poids lourd. Boost est assez léger en ce que vous pouvez inclure exactement les bits dont vous avez besoin, sans dépendances sur le reste de la collection de la bibliothèque.

D'après ce que vous avez décrit (une application légère pour une large base d'utilisateurs), il n'y a nulle raison de ne pas utiliser Boost. Il peut simplifier votre code et réduire le nombre de bugs causés par la tentative de réinventer la roue. Lors de la distribution de l'exécutable compilé, il a un coût nul. La bibliothèque Boost.Any est, comme beaucoup de Boost, d'en-tête uniquement, et est tout simplement compilé dans votre exécutable. Aucune bibliothèque séparées doivent être distribués.

Il n'y a rien à gagner en essayant de réinventer la roue. Votre fichier exécutable sera pas plus petit ou plus efficace, mais plus buggy.

Et je suis prêt à parier que votre solution brassée maison sera pas être ANSI C ++. Il reposera sur une certaine forme de comportement non défini. Si vous voulez une solution ANSI-C ++, votre meilleur pari est Boost.

Vous pouvez utiliser une 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 Source Libraries a aussi adobe::any_regular_t , ce qui vous permet de stocker tout type aussi longtemps comme il modélise concept de régulier . Vous enveloppez votre valeur de retour de la même façon que vous le feriez avec boost::any. (Il y a aussi une documentation sur la page liée à la façon dont adobe::any_regular_t diffère de boost::any -. Bien sûr le type que vous choisissez doit dépendre des exigences de votre code)

Vous pouvez passer par référence au lieu et être typesave et vérifier si cela a fonctionné en même temps, ne comporterait pas de bibliothèque supplémentaire soit (votre type de 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;
}

...

Je trouve cette solution souvent plus propre en termes de design. Il est regrettable que les signatures de funciton ne permettent pas plusieurs types comme types de retour, mais cette façon vous pouvez passer quoi que ce soit.

Si l'utilisateur sait ce qui est mis en, vous pouvez utiliser un modèle pour résoudre ce problème. Sinon, je ne peux pas penser à une solution.

Je pense que le problème est au sujet de cette conception de la fonction. Avez-vous essayé la surcharge?

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

Dans une fonction doIT vous pouvez placer plus de code et de les faire appeler d'autres fonctions pour ne pas répéter le code.

= CHOSE void *

Vous devez jeter la valeur retournée, vous devez savoir ce qui est renvoyé.

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;
        }
    }
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top