2010-04-23 11 views
7

"Introduzione"C++ e il suo sistema di tipi: come trattare i dati con più tipi?

Sono relativamente nuovo al C++. Ho esaminato tutte le informazioni di base e sono riuscito a creare 2-3 semplici interpreti per i miei linguaggi di programmazione.

La prima cosa che ha dato e ancora mi dà il mal di testa: Implementare il sistema di tipi del mio linguaggio in C++

Pensate che: Ruby, Python, PHP e Co. hanno un sacco di built-in tipi che ovviamente sono implementati in C. Quindi quello che ho provato prima era di dare un valore nella mia lingua a tre possibili tipi: Int, String e Nil.

sono arrivato fino a questo:

enum ValueType 
{ 
    Int, String, Nil 
}; 

class Value 
{ 
public: 
    ValueType type; 
    int intVal; 
    string stringVal; 
}; 

Sì, wow, lo so. È stato estremamente lento passare questa lezione mentre l'allocatore di stringhe doveva essere chiamato sempre.

prossima volta ho provato qualcosa di simile a questo:

enum ValueType 
{ 
    Int, String, Nil 
}; 

extern string stringTable[255]; 
class Value 
{ 
public: 
    ValueType type; 
    int index; 
}; 

vorrei memorizzare tutte le stringhe in stringTable e scrivere la loro posizione a index. Se il tipo di Value era Int, ho appena memorizzato il numero intero in index, non avrebbe alcun senso utilizzare un indice int per accedere a un altro int, o?

Ad ogni modo, anche questo mi ha dato un mal di testa. Dopo un po 'di tempo, accedendo alla stringa dal tavolo qui, facendo riferimento a quello lì e copiandolo lì è cresciuto sopra la mia testa - ho perso il controllo. Ho dovuto mettere giù la bozza dell'interprete.

Ora: Ok, quindi C e C++ sono tipizzati staticamente.

  • Come le principali implementazioni delle lingue di cui sopra trattano i diversi tipi nei loro programmi (fixnums, bignum, nums, stringhe, array, risorse, ...)?

  • Cosa devo fare per ottenere la massima velocità con molti tipi diversi disponibili?

  • Come si confrontano le soluzioni con le versioni semplificate di cui sopra?

+0

"Int, String, Nil", che dire di galleggiante ? – hhafez

+1

Suppongo pienamente l'idea di un linguaggio di programmazione senza 'float'! Altrimenti la prima domanda sub_Language su SO sarà "Ehi, perché non 0.1 + 0.2 == 0.3 in sub_Language? È rotto!". – bobince

+0

@sub: se sei quello che esegue il downvoting di alcune risposte, dovresti riconsiderarlo. Ci sono risposte valide che sono state downvoted senza motivo, alcune delle quali potrebbero essere che non hai capito la risposta in tutta la loro estensione. Se non sei stato tu, a chiunque l'abbia fatto: per favore, spiega quello che consideri sbagliato dalle risposte. Questo è l'unico modo per migliorare effettivamente il sistema. –

risposta

4

Ci sono un paio di cose diverse che puoi fare qui. Diverse soluzioni sono arrivate nel tempo e la maggior parte di esse richiede l'allocazione dinamica del dato attuale (boost :: variant può evitare di utilizzare la memoria allocata dinamicamente per gli oggetti di piccole dimensioni - grazieHMSalters).

approccio C Pure:

Store Tipo di informazioni e un puntatore nullo alla memoria che deve essere interpretata in base alle informazioni di tipo (di solito un enum):

enum type_t { 
    integer, 
    string, 
    null 
}; 
typedef struct variable { 
    type_t type; 
    void * datum; 
} variable_t; 
void init_int_variable(variable_t * var, int value) 
{ 
    var->type = integer; 
   var->datum = malloc(sizeof(int)); 
    *((int)var->datum) = value; 
} 
void fini_variable(variable_t var) // optionally by pointer 
{ 
    free(var.datum); 
} 

In C++ è possibile migliorare questo approccio utilizzando le classi per semplificare l'utilizzo, ma soprattutto è possibile utilizzare soluzioni più complesse e utilizzare le librerie esistenti come boost :: any o boost :: variant che offrono diverse soluzioni allo stesso problema.

Sia boost :: any che boost :: variant memorizzano i valori nella memoria allocata dinamicamente, in genere tramite un puntatore a una classe virtuale in una gerarchia e con operatori che reinterpretano (verso il basso) i tipi concreti.

+0

Non penso che boost :: variant (o anche boost :: any) _require_ allocazione dinamica. Hanno trucchi speciali per aggirare questo quando possibile. In parole povere, anche se hanno un 'void *' per i grandi oggetti sull'heap, quel puntatore può essere parte di un'unione i cui altri membri sono usati per contenere piccoli tipi di dati. – MSalters

+0

@MSalters: giusto. Non avevo esaminato l'implementazione della variante e utilizza un buffer che viene reinterpretato a seconda del tipo particolare utilizzato, come hai spiegato. D'altro canto, l'aumento è molto più semplice e utilizza la memoria allocata dinamicamente incondizionatamente. –

1

Per quanto riguarda la velocità, si dicono:

era estremamente lento a passare questa classe intorno come l'allocatore di stringa doveva essere chiamato per tutto il tempo.

Lo sai che si dovrebbe essere passando gli oggetti per riferimento la maggior parte del tempo? La tua soluzione sembra funzionante per un semplice interprete.

+0

Non è stato così facile la maggior parte del tempo. Ho dovuto fare molte copie quante funzioni modificano temporaneamente i valori. – sub

+1

@sub Beh, sembra discutibile. Non riesco a capire perché cambieresti mai un valore, una volta creato, a meno che l'utente non lo stia assegnando. –

4

Una soluzione ovvia è quella di definire un tipo di gerarchia:

class Type 
{ 
}; 

class Int : public Type 
{ 
}; 

class String : public Type 
{ 
}; 

e così via.Come esempio completo, scriviamo un interprete per un linguaggio minuscolo. Il linguaggio permette di dichiarazione delle variabili in questo modo:

var a 10 

che creerà un oggetto Int, assegnare il valore 10 e memorizzarlo nella tabella di una variabile sotto il nome di a. Le operazioni possono essere invocate su variabili. Per esempio l'operazione di addizione su due valori Int assomiglia:

+ a b 

Ecco il codice completo per l'interprete:

#include <iostream> 
#include <string> 
#include <vector> 
#include <sstream> 
#include <cstdlib> 
#include <map> 

// The base Type object from which all data types are derived. 
class Type 
{ 
public: 
    typedef std::vector<Type*> TypeVector; 
    virtual ~Type() { } 

    // Some functions that you may want all types of objects to support: 

    // Returns the string representation of the object. 
    virtual const std::string toString() const = 0; 
    // Returns true if other_obj is the same as this. 
    virtual bool equals (const Type &other_obj) = 0; 
    // Invokes an operation on this object with the objects in args 
    // as arguments. 
    virtual Type* invoke (const std::string &opr, const TypeVector &args) = 0; 
}; 

// An implementation of Type to represent an integer. The C++ int is 
// used to actually store the value. As a consequence this type is 
// machine dependent, which might not be what you want for a real 
// high-level language. 
class Int : public Type 
{ 
public: 
    Int() : value_ (0), ret_ (NULL) { } 
    Int (int v) : value_ (v), ret_ (NULL) { } 
    Int (const std::string &v) : value_ (atoi (v.c_str())), ret_ (NULL) { } 
    virtual ~Int() 
    { 
    delete ret_; 
    } 
    virtual const std::string toString() const 
    { 
    std::ostringstream out; 
    out << value_; 
    return out.str(); 
    } 
    virtual bool equals (const Type &other_obj) 
    {  
    if (&other_obj == this) 
     return true; 
    try 
     { 
     const Int &i = dynamic_cast<const Int&> (other_obj); 
     return value_ == i.value_; 
     } 
    catch (std::bad_cast ex) 
     { 
     return false; 
     } 
    } 
    // As of now, Int supports only addition, represented by '+'. 
    virtual Type* invoke (const std::string &opr, const TypeVector &args)  
    { 
    if (opr == "+") 
     { 
     return add (args); 
     } 
    return NULL; 
    } 
private: 
    Type* add (const TypeVector &args) 
    { 
    if (ret_ == NULL) ret_ = new Int; 
    Int *i = dynamic_cast<Int*> (ret_); 
    Int *arg = dynamic_cast<Int*> (args[0]); 
    i->value_ = value_ + arg->value_; 
    return ret_; 
    } 
    int value_; 
    Type *ret_; 
}; 

// We use std::map as a symbol (or variable) table. 
typedef std::map<std::string, Type*> VarsTable; 
typedef std::vector<std::string> Tokens; 

// A simple tokenizer for our language. Takes a line and 
// tokenizes it based on whitespaces. 
static void 
tokenize (const std::string &line, Tokens &tokens) 
{ 
    std::istringstream in (line, std::istringstream::in); 
    while (!in.eof()) 
    { 
     std::string token; 
     in >> token; 
     tokens.push_back (token); 
    } 
} 

// Maps varName to an Int object in the symbol table. To support 
// other Types, we need a more complex interpreter that actually infers 
// the type of object by looking at the format of value. 
static void 
setVar (const std::string &varName, const std::string &value, 
     VarsTable &vars) 
{ 
    Type *t = new Int (value); 
    vars[varName] = t; 
} 

// Returns a previously mapped value from the symbol table. 
static Type * 
getVar (const std::string &varName, const VarsTable &vars) 
{ 
    VarsTable::const_iterator iter = vars.find (varName); 
    if (iter == vars.end()) 
    { 
     std::cout << "Variable " << varName 
       << " not found." << std::endl; 
     return NULL; 
    } 
    return const_cast<Type*> (iter->second); 
} 

// Invokes opr on the object mapped to the name var01. 
// opr should represent a binary operation. var02 will 
// be pushed to the args vector. The string represenation of 
// the result is printed to the console. 
static void 
invoke (const std::string &opr, const std::string &var01, 
     const std::string &var02, const VarsTable &vars) 
{ 
    Type::TypeVector args; 
    Type *arg01 = getVar (var01, vars); 
    if (arg01 == NULL) return; 
    Type *arg02 = getVar (var02, vars); 
    if (arg02 == NULL) return; 
    args.push_back (arg02); 
    Type *ret = NULL; 
    if ((ret = arg01->invoke (opr, args)) != NULL) 
    std::cout << "=> " << ret->toString() << std::endl; 
    else 
    std::cout << "Failed to invoke " << opr << " on " 
       << var01 << std::endl; 
} 

// A simple REPL for our language. Type 'quit' to exit 
// the loop. 
int 
main (int argc, char **argv) 
{ 
    VarsTable vars; 
    std::string line; 
    while (std::getline (std::cin, line)) 
    { 
     if (line == "quit") 
     break; 
     else 
     { 
      Tokens tokens; 
      tokenize (line, tokens); 
      if (tokens.size() != 3) 
      { 
       std::cout << "Invalid expression." << std::endl; 
       continue; 
      } 
      if (tokens[0] == "var") 
      setVar (tokens[1], tokens[2], vars); 
      else 
      invoke (tokens[0], tokens[1], tokens[2], vars); 
     } 
    } 
    return 0; 
} 

Un esempio di interazione con l'interprete:

/home/me $ ./mylang 

var a 10 
var b 20 
+ a b 
30 
+ a c 
Variable c not found. 
quit 
+0

Posso memorizzarli tutti in un unico array? O sono tipi completamente diversi come string vs int? Sono relativamente nuovo a questo. – sub

+0

+1 @Vijay Mathew: I miei pensieri esattamente – hhafez

+0

@Vijay: Potresti spiegare come e dove archiviare le istanze delle classi? – sub

1

C++ è un linguaggio fortemente tipizzato. Vedo che provieni da un linguaggio non tipizzato e continui a pensare in questi termini.

Se si in realtà è necessario memorizzare diversi tipi in una variabile, quindi dare un'occhiata a boost::any.

Tuttavia, se si sta implementando un interprete si dovrebbe utilizzare l'ereditarietà e le classi che rappresentano un tipo specifico.

+0

Questo non è un mio problema. Non mi hai detto * come * e * dove * memorizzare le istanze di quelle classi. – sub

+0

Ho svitato, poiché la risposta è buona: usa boost :: any. Non riesco a vedere come non è il tuo problema. –

+0

+1, d'accordo. boost :: any è una soluzione alla domanda scritta, anche se non è una risposta (completa) alla domanda in sub-head. – MSalters

0

Secondo la soluzione di Vijay l'attuazione sarà:

Type* array; 
// to initialize the array 
array = new Type(size_of_array); 
// when you want to add values 
array[0] = new Int(42); 
// to add another string value 
array[1] = new String("fourty two"); 

Il bit manca dal suo codice è come estrarre quei valori ... Ecco la mia versione (in realtà ho imparato questo da Ogre e modificato al mio gradire).

Usage è qualcosa di simile:

Any array[4]; 
// Automatically understands it's an integer 
array[0] = Any(1); 
// But let's say you want the number to be thought of as float 
array[1] = Any<float>(2); 
// What about string? 
array[2] = Any<std::string>("fourty two"); 
// Note that this gets the compiler thinking it's a char* 
// instead of std::string 
array[3] = Any("Sometimes it just turns out to be what you don't want!"); 

Ok, ora per vedere se un particolare elemento è una stringa:

if(array[2].isType<std::string>() 
{ 
    // Extract the string value. 
    std::string val = array[2].cast<std::string>(); 
    // Make the string do your bidding!!!... /evilgrin 
    // WAIT! But what if you want to directly manipulate 
    // the value in the array? 
    std::string& val1 = array[2].cast<std::string>(); 
    // HOHOHO... now any changes to val1 affects the value 
    // in the array ;) 
} 

Il codice per la Ogni classe è riportata qui sotto. Sentiti libero di usarlo come preferisci :). Spero che questo ti aiuti!

Nel file di intestazione ... diciamo Any.h

#include <typeinfo> 
    #include <exception> 

    /* 
    * \class Any 
    * \brief A variant type to hold any type of value. 
    * \detail This class can be used to store values whose types are not 
    *  known before hand, like to store user-data. 
    */ 
    class Any 
    { 
    public: 
     /*! 
     * \brief Default constructor. 
     */ 

    Any(void); 

    /*! 
    * \brief Constructor that accepts a default user-defined value. 
    * \detail This constructor copies that user-defined value into a 
    *  place holder. This constructor is explicit to avoid the compiler 
    *  to call this constructor implicitly when the user didn't want 
    *  the conversion to happen. 
    * \param val const reference to the value to be stored. 
    */ 
    template <typename ValueType> 
    explicit Any(const ValueType& val); 

    /*! 
    * \brief Copy constructor. 
    * \param other The \c Any variable to be copied into this. 
    */ 
    Any(const Any& other); 

    /*! 
    * \brief Destructor, does nothing other than destroying the place holder. 
    */ 
    ~Any(void); 

    /*! 
    * \brief Gets the type of the value stored by this class. 
    * \detail This function uses typeid operator to determine the type 
    *  of the value it stores. 
    * \remarks If the place holder is empty it will return Touchscape::VOID_TYPE. 
    *  It is wise to check if this is empty by using the function Any::isEmpty(). 
    */ 
    const std::type_info& getType() const; 

    /*! 
    * \brief Function to verify type of the stored value. 
    * \detail This function can be used to verify the type of the stored value. 
    * Usage: 
    * \code 
    * int i; 
    * Touchscape::Any int_any(i); 
    * // Later in your code... 
    * if (int_any.isType<int>()) 
    * { 
    *  // Do something with int_any. 
    * } 
    * \endcode 
    * \return \c true if the type matches, false otherwise. 
    */ 
    template <typename T> 
    bool isType() const; 

    /*! 
    * \brief Checks if the type stored can be converted 'dynamically' 
    *  to the requested type. 
    * \detail This would useful when the type stored is a base class 
    *  and you would like to verify if it can be converted to type 
    *  the user wants. 
    * Example: 
    * \code 
    * class Base 
    * { 
    *  // class implementation. 
    * }; 
    * class Derived : public Base 
    * { 
    *  // class implementation. 
    * }; 
    * 
    * // In your implementation function. 
    * { 
    *  //... 
    *  // Somewhere in your code. 
    *  Base* a = new Derived(); 
    *  Touchscape::Any user_data(a); 
    *  my_object.setUserData(user_data); 
    *  // Then when you need to know the user-data type 
    *  if(my_object.getUserData().isDynamicType<Derived>()) 
    *  { 
    *   // Do something with the user data 
    *  } 
    * } 
    * \endcode 
    * \return \c true if the value stored can be dynamically casted to the target type. 
    * \deprecated This function will be removed and/or changed in the future. 
    */ 
    template <typename T> 
    bool isDynamicType() const; 

    /*! 
    * \brief Convert the value stored to the required type. 
    * \detail This function is used just like a static-cast to retrieve 
    *  the stored value. 
    * \return A reference to the stored value. 
    * \warning This function will throw std::bad_cast exception if it 
    *  finds the target type to be incorrect. 
    */ 
    template <typename T> 
    T& cast(); 

    /*! 
    * \brief Convert the value stored to the required type (const version). 
    * \detail This function is used just like static_cast to retrieve 
    *  the stored value. 
    * \return A \c const reference to the stored value. 
    * \warning This function will throw std::bad_cast exception if it 
    *  finds the target type to be incorrect. 
    */ 
    template <typename T> 
    const T& cast() const; 

    /*! 
    * \brief Dynamically converts the stored value to the target type 
    * \detail This function is just like dynamic_cast to retrieve 
    *  the stored value to the target type. 
    * \return A reference to the stored value. 
    * \warning This function will throw std::bad_cast exception if it 
    *  finds that the value cannot be dynamically converted to the target type. 
    * \deprecated This function will be removed and/or changed in the future. 
    */ 
    template <typename T> 
    T& dynamicCast(); 

    /*! 
    * \brief Dynamically converts the stored value to the target type (const version) 
    * \detail This function is just like dynamic_cast to retrieve 
    *  the stored value to the target type. 
    * \return A const reference to the stored value. 
    * \warning This function will throw std::bad_cast exception if it 
    *  finds that the value cannot be dynamically converted to the target type. 
    * \deprecated This function will be removed and/or changed in the future. 
    */ 
    template <typename T> 
    const T& dynamicCast() const; 

    /*! 
    * \brief Swaps the contents with another \c Any variable. 
    * \return reference to this instance. 
    */ 
    Any& swap(Any& other); 

    /*! 
    * \brief Checks if the place holder is empty. 
    * \return \c true if the the place holder is empty, \c false otherwise. 
    */ 
    bool isEmpty() const; 

    /*! 
    * \brief Checks if the place holder is \b not empty. 
    * \return \c true if the the place holder is not empty, \c false otherwise. 
    * \remarks This is just a lazy programmer's attempt to make the code look elegant. 
    */ 
    bool isNotEmpty() const; 

    /*! 
    * \brief Assignment operator 
    * \detail Assigns a 'raw' value to this instance. 
    * \return Reference to this instance after assignment. 
    */ 
    template <typename ValueType> 
    Any& operator = (const ValueType& rhs); 

    /*! 
    * \brief Default assignment operator 
    * \detail Assigns another \c Any type to this one. 
    * \return Reference to this instance after assignment. 
    */ 
    Any& operator = (const Any& rhs); 

    /*! 
    * \brief Boolean equality operator 
    */ 
    bool operator == (const Any& other) const; 

    /*! 
    * \brief Boolean equality operator that accepts a 'raw' type. 
    */ 
    template<typename ValueType> 
    bool operator == (const ValueType& other) const; 

    /*! 
    * \brief Boolean inequality operator 
    */ 
    bool operator != (const Any& other) const; 

    /*! 
    * \brief Boolean inequality operator that accepts a 'raw' type. 
    */ 


     template<typename ValueType> 
     bool operator != (const ValueType& other) const; 
    protected: 
     /*! 
     * \class PlaceHolder 
     * \brief The place holder base class 
     * \detail The base class for the actual 'type'd class that stores 
     *  the value for T 

ouchscape::Any. 
    */ 
    class PlaceHolder 
    { 
    public: 

     /*! 
     * \brief Virtual destructor. 
     */ 
     virtual ~PlaceHolder(){} 

     /*! 
     * \brief Gets the \c type_info of the value stored. 
     * \return (const std::type_info&) The typeid of the value stored. 
     */ 
     virtual const std::type_info& getType() const = 0; 
     /*! 
     * \brief Clones this instance. 
     * \return (PlaceHolder*) Cloned instance. 
     */ 
     virtual PlaceHolder* clone() const = 0; 
    }; 

    /*! 
    * \class PlaceHolderImpl 
    * \brief The class that ultimately keeps hold of the value stored 
    *  in Touchscape::Any. 
    */ 
    template <typename ValueType> 
    class PlaceHolderImpl : public PlaceHolder 
    { 
    public: 
     /*! 
     * \brief The only constructor allowed. 
     * \param val The value to store. 
     */ 
     PlaceHolderImpl(const ValueType& val) 
      :m_value(val){} 
     /*! 
     * \brief The destructor. 
     * \detail Does nothing 
     */ 
     ~PlaceHolderImpl(){} 

     /*! 
     * \copydoc Touchscape::PlaceHolder::getType() 
     */ 
     const std::type_info& getType() const 
     { 
      return typeid(ValueType); 
     } 

     /*! 
     * \copydoc Touchscape::PlaceHolder::clone() 
     */ 
     PlaceHolder* clone() const 
     { 
      return new PlaceHolderImpl<ValueType>(m_value); 
     } 

     ValueType m_value; 
    }; 

    PlaceHolder* m_content; 
}; 

/************************************************************************/ 
/* Template code implementation section         */ 
/************************************************************************/ 
template <typename ValueType> 
Any::Any(const ValueType& val) 
    :m_content(new PlaceHolderImpl<ValueType>(val)) 
{ 
} 
//--------------------------------------------------------------------- 
template <typename T> 
bool Any::isType() const 
{ 
    bool result = m_content?m_content->getType() == typeid(T):false; 
    return result; 
} 
//--------------------------------------------------------------------- 
template <typename T> 
bool Any::isDynamicType() const 
{ 
    bool result = m_content 
     ?dynamic_cast<T>(static_cast<PlaceHolderImpl<T>*>(m_content)->m_value)!=NULL 
     :false; 
    return result; 
} 
//--------------------------------------------------------------------- 
template <typename T> 
T& Any::cast() 
{ 
    if (getType() != VOID_TYPE && isType<T>()) 
    { 
     T& result = static_cast<PlaceHolderImpl<T>*>(m_content)->m_value; 
     return result; 
    } 
    StringStream ss; 
    ss<<"Cannot convert '"<<getType().name()<<"' to '"<<typeid(T).name()<<"'. Did you mean to use dynamicCast() to cast to a different type?"; 
    throw std::bad_cast(ss.str().c_str()); 
} 
//--------------------------------------------------------------------- 
template <typename T> 
const T& Any::cast() const 
{ 
    Any& _this = const_cast<Any&>(*this); 
    return _this.cast<T>(); 
} 
//--------------------------------------------------------------------- 
template <typename T> 
T& Any::dynamicCast() 
{ 
    T* result = dynamic_cast<T>(static_cast<PlaceHolderImpl<T>*>(m_content)->m_value); 
    if (result == NULL) 
    { 
     StringStream ss; 
     ss<<"Cannot convert '"<<getType().name()<<"' to '"<<typeid(T)<<"'."; 
     throw std::bad_cast(ss.str().c_str()); 
    } 
    return *result; 
} 
//--------------------------------------------------------------------- 
template <typename T> 
const T& Any::dynamicCast() const 
{ 
    Any& _this = const_cast<Any&>(*this); 
    return _this.dynamicCast<T>(); 
} 
//--------------------------------------------------------------------- 
template <typename ValueType> 
Any& Any::operator = (const ValueType& rhs) 
{ 
    Any(rhs).swap(*this); 
    return *this; 
} 
//--------------------------------------------------------------------- 
template <typename ValueType> 
bool Any::operator == (const ValueType& rhs) const 
{ 
    bool result = m_content == rhs; 
    return result; 
} 
//--------------------------------------------------------------------- 
template <typename ValueType> 
bool Any::operator != (const ValueType& rhs) const 
{ 
    bool result = m_content != rhs; 
    return result; 
} 

Ora nel file CPP ... Any.cpp

#include "Any.h" 

static const std::type_info& VOID_TYPE(typeid(void)); 

Any::Any(void) 
    :m_content(NULL) 
{ 
} 
//--------------------------------------------------------------------- 
Any::Any(const Any& other) 
    :m_content(other.m_content?other.m_content->clone():NULL) 
{ 
} 
//--------------------------------------------------------------------- 
Any::~Any(void) 
{ 
    SafeDelete(m_content); 
} 
//--------------------------------------------------------------------- 
const std::type_info& Any::getType() const 
{ 
    return m_content?m_content->getType():VOID_TYPE; 
} 
//--------------------------------------------------------------------- 
Any& Any::swap(Any& other) 
{ 
    std::swap(m_content, other.m_content); 
    return *this; 
} 
//--------------------------------------------------------------------- 
Any& Any::operator=(const Any& rhs) 
{ 
    Any(rhs).swap(*this); 
    return *this; 
} 
//--------------------------------------------------------------------- 
bool Any::isEmpty() const 
{ 
    bool is_empty = m_content == NULL; 
    return is_empty; 
} 
//--------------------------------------------------------------------- 
bool Any::isNotEmpty() const 
{ 
    bool is_not_empty = m_content != NULL; 
    return is_not_empty; 
} 
//--------------------------------------------------------------------- 
bool Any::operator==(const Any& other) const 
{ 
    bool result = m_content == other.m_content; 
    return result; 
} 
//--------------------------------------------------------------------- 
bool Any::operator!=(const Any& other) const 
{ 
    bool result = m_content != other.m_content; 
    return result; 
} 
+0

Ho dimenticato di menzionare. In termini di velocità, è effettivamente un puntatore all'istanza di PlaceHolder, che memorizza il puntatore sul valore che viene passato. Non troppo TROPPO credo. Ed è sicuro per i caratteri e non ha bisogno di librerie complesse come Boost. –