Modification du type de retour d'une fonction sans spécialisation de modèle. C ++

StackOverflow https://stackoverflow.com/questions/1010539

  •  06-07-2019
  •  | 
  •  

Question

Je me demandais s'il était possible de changer le type de retour d'une fonction en fonction du type de variable à laquelle elle est assignée. Voici un exemple rapide de ce que je veux dire.

Je souhaite créer une fonction qui analyse une variable de type int, bool ou float à partir d'une chaîne. Par exemple ...

Int value = parse("37");
Float value = parse("3.14");
Bool value = parse("true");

Je comprends que si je transforme cette fonction en modèle, le type de variable doit être déterminé à partir de la liste des arguments, qui sera toujours une chaîne. Existe-t-il un autre moyen de faire cela avec c ++?

Était-ce utile?

La solution

Ceci peut être fait avec une fonction de conversion

struct proxy {
    string str;
    proxy(string const &str):str(str) { }
    template<typename T> operator T() { 
        return boost::lexical_cast<T>(str); 
    }
};

proxy parse(string const &str) { return proxy(str); }

Il ne vous reste plus qu'à faire

float a = parse("3.1");

Et cela devrait bien fonctionner. Incidemment, vous pouvez simplement utiliser la classe directement. Je recommande de le renommer en conversion_proxy pour souligner le fait qu'il ne s'agit que d'un proxy pour une conversion en cours, mais qu'il ne fait pas lui-même la conversion

struct conversion_proxy {
    string str;
    conversion_proxy(string const &str):str(str) { }
    template<typename T> operator T() { 
        return boost::lexical_cast<T>(str); 
    }
};

float a = conversion_proxy("3.1"); 

Autres conseils

Je ne peux pas dire d'après votre question si vous le savez ou non, mais vous pouvez le faire avec un modèle. Le seul problème est que vous devrez spécifier le type à partir duquel vous convertissez dans chaque invocation au lieu de vous fier à l'inférence (puisque vous avez dit que le type d'argument sera toujours le même).

template<typename T> T parse(const string& str) { /* do stuff for other types */ }
template<> int parse<int>(const string& str) { /* do stuff for ints */ }
template<> double parse<double>(const string& str) { /* do stuff for doubles */ }
template<> bool parse<bool>(const string& str) { /* do stuff for bools */ }
// etc.

Et invoquer ensuite comme

int value = parse<int>("37");
double value = parse<double>("3.14");
bool value = parse<bool>("true");

Si vous le saviez déjà, ignorez simplement cette réponse, mais votre question ne dit pas clairement que vous savez que cela est possible.

Bien sûr, si ce que vous faites n'est pas vraiment générique (et que vous devez donc vous spécialiser pour chaque type à analyser), alors écrire un modèle n'est pas la bonne chose à faire de toute façon.

À propos, vous pouvez le faire assez généralement avec une seule fonction comme celle-ci (en supposant que l'analyse soit ce que vous voulez vraiment faire):

#include <sstream>
template<typename T> T parse(const string& str) 
{
  T t;
  std::istringstream sstr(str);
  sstr >> t;
  return t;
}

Ceci fonctionnera pour tout type extractible par flux, constructible par défaut, qui inclut tous les éléments intégrés.

Vous pouvez transmettre votre argument de sortie en tant que pointeur ou référence.

Comme ceci:

template<class T> void parse(const std::string &input, T& output);

Puis, codez comme ceci:

double d; parse(input, d);
int i; parse(input, i);

devrait fonctionner.

Cependant, votre code ressemble parfaitement à un std :: istringstream qui serait simplement:

istringstream is(input);
input >> d;

Si la mise en forme est un peu compliquée, une astuce qui me convient très bien consiste à créer des objets personnalisés avec un opérateur personnalisé > > qui extrait les données.

Ensuite, cela pourrait être comme:

istringstring is(input);
input >> LineExtracter(x, y, d);

Je suis d’accord avec Litb qui a été un peu plus rapide que moi. Utilisez les opérateurs de casting.

#include <iostream>
#include <string>
#include <sstream>

class Convertible
{
public:
    int m_Integer;
    bool m_Bool;
    double m_Double;

    Convertible() : m_Integer(0), m_Bool(false), m_Double(0.0) {};

    operator int() const
    {
        return m_Integer;
    }
    operator bool() const
    {
        return m_Bool;
    }
    operator double() const
    {
        return m_Double;
    }
};

Convertible parse(std::string data)
{
    Convertible l_result;

    std::istringstream converter(data);
    converter >> l_result.m_Integer;

    std::istringstream converter2(data);
    converter2 >> l_result.m_Bool;

    std::istringstream converter3(data);
    converter3 >> l_result.m_Double;

    return l_result;
}

void main()
{
    int l_convertedInt = parse("2");
    bool l_convertedBool = parse("true");
    double l_convertedDouble = parse("3.14");

    std::cout << "Converted '2' to " << l_convertedInt << std::endl;
    std::cout << "Converted 'true' to " << l_convertedBool << std::endl;
    std::cout << "Converted '3.14' to " << l_convertedDouble << std::endl;
}

Malheureusement, ce n'est pas possible. En C ++, il n'est pas possible de surcharger une fonction en fonction de sa valeur de retour. Vous devez avoir 3 fonctions, ParseInt, ParseFloat et ParseBool, ou utiliser un modèle de fonction.

Vous pouvez retourner void *, puis diffuser le résultat si nécessaire.

Je déconseille cependant cela. C ++ est un langage fortement typé. L'avantage de ceci est que le compilateur peut détecter les erreurs plus tôt qu'un langage typé dynamiquement.

Non, ce type de comportement n'est pas possible en C ++. Pour être admissible, il faudrait pouvoir définir des fonctions du même nom dans la même étendue, qui ne diffèrent que par le type de retour. Ce n'est pas légal en C ++.

C ++ peut effectuer certaines spécialisations de types de retour, telles que des types de retour covariants sur des fonctions virtuelles remplacées. Mais cela ne prend pas en charge ce que vous recherchez.

Voici mon adaptation de la réponse de @Tyler McHenry à ma situation où l'argument de parse () est un type autre qu'une chaîne.

Notez que j'ai dû introduire une spécialisation de modèle afin d'éviter un avertissement de conversion de type ( float vers int. ).

(Voir également la démonstration en direct .)

#include <iostream>

struct MyUnion
{
public:
  union {
    bool bool_value;
    int int_value;
    float float_value;
  };
};

template<typename T> T parse(const MyUnion& h)
{
  T t;

  if (typeid(T) == typeid(bool)) {
    t = h.bool_value;
  } else if (typeid(T) == typeid(int)) {
    t = h.int_value;
  } else if (typeid(T) == typeid(float)) {
    // t = h.float_value; // see **Warning** below; use float specialization instead
  }

  return t;
}

// 'float' template specialization to avoid conversion warning.
template<> float parse(const MyUnion& h)
{
  return h.float_value;
}

int main()
{
  MyUnion mu1; mu1.bool_value = true;
  MyUnion mu2; mu2.int_value = 42;
  MyUnion mu3; mu3.float_value = 3.14159;

  std::cout << "As bool: "  << parse<bool>(mu1)  << std::endl;
  std::cout << "As int: "   << parse<int>(mu2)   << std::endl;
  std::cout << "As float: " << parse<float>(mu3) << std::endl;
}

// **Warning**
// In function 'T parse(const Heterogeneous&) [with T = int]':
// Line 22: warning: converting to 'int' from 'const float'
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top