Modifica del tipo restituito di una funzione senza specializzazione del modello. C ++

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

  •  06-07-2019
  •  | 
  •  

Domanda

Mi chiedevo se fosse possibile cambiare il tipo di ritorno di una funzione in base al tipo di variabile a cui è stata assegnata. Ecco un breve esempio di cosa intendo.

Voglio creare una funzione che analizza una variabile di int, bool o float da una stringa. Ad esempio ...

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

Capisco se faccio di questa funzione un modello, che il tipo di variabile deve essere determinato dalla lista degli argomenti che sarà sempre una stringa. Esiste un altro modo per farlo con c ++?

È stato utile?

Soluzione

Questo può essere fatto con una funzione di conversione

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

Ora devi solo fare

float a = parse("3.1");

E dovrebbe funzionare bene. Per inciso, puoi semplicemente usare la classe direttamente. Consiglio di rinominarlo in conversion_proxy per indicare il fatto che è solo un proxy per una conversione in corso ma che di per sé non esegue la conversione

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

Altri suggerimenti

Non posso dire dalla tua domanda se lo sai o no, ma puoi davvero farlo con un modello. L'unico problema è che dovrai specificare il tipo da cui stai convertendo in ogni invocazione invece di fare affidamento sull'inferenza (poiché, come hai detto, il tipo di argomento sarà sempre lo stesso).

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.

E quindi invocare come

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

Se lo sapevi già, ignora questa risposta, ma dalla tua domanda non è chiaro che sei consapevole che ciò è possibile.

Naturalmente, se quello che stai facendo non è veramente generico (e quindi devi specializzarti per ogni tipo che vuoi analizzare), scrivere un modello non è la cosa giusta da fare comunque.

A proposito, puoi farlo abbastanza genericamente con una singola funzione come questa (supponendo che l'analisi sia ciò che vuoi davvero fare):

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

Funzionerà con qualsiasi tipo costruibile per default, estraibile in streaming, che include tutti i built-in.

Potresti passare l'argomento di output come puntatore o riferimento.

In questo modo:

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

Quindi codice in questo modo:

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

dovrebbe funzionare.

Tuttavia, il tuo codice sembra perfetto per uno std :: istringstream che sarebbe:

istringstream is(input);
input >> d;

Se hai una formattazione piuttosto complicata, un trucco che ho avuto abbastanza fortuna con la creazione di oggetti personalizzati con operatore personalizzato > > che estrae i dati.

Quindi potrebbe essere come:

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

Concordo con Litb che è stato un po 'più veloce di me. Usa gli operatori di 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;
}

Sfortunatamente, ciò non è possibile. In C ++ non è possibile sovraccaricare una funzione in base al valore restituito. Devi avere 3 funzioni, ParseInt, ParseFloat e ParseBool, oppure utilizzare un modello di funzione.

È possibile restituire void * e quindi trasmettere il risultato secondo necessità.

Sconsiglio però. C ++ è un linguaggio fortemente tipizzato. Il vantaggio è che il compilatore può rilevare errori prima di un linguaggio tipizzato in modo dinamico.

No, questo tipo di comportamento non è possibile in C ++. Per essere ammissibile, sarebbe necessario essere in grado di definire funzioni con lo stesso nome nello stesso ambito che differivano solo per tipo di ritorno. Questo non è legale in C ++.

Il C ++ può eseguire alcune specializzazioni del tipo di ritorno come tipi di ritorno covarianti su funzioni virtuali sovrascritte. Ma non supporta ciò che stai cercando.

Ecco il mio adattamento di @Tyler McHenry's answer per la mia situazione in cui l'argomento su parse () è un tipo diverso da una stringa.

Nota che ho scoperto che dovevo introdurre una specializzazione modello per evitare un avviso di conversione del tipo ( float in int ).

(vedi anche demo live .)

#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'
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top