2010-04-23 6 views
7

"Introducción"C++ y su sistema de tipos: ¿cómo tratar los datos con varios tipos?

Soy relativamente nuevo en C++. Revisé todo lo básico y logré construir 2-3 intérpretes simples para mis lenguajes de programación.

Lo primero que dio y todavía me da un dolor de cabeza: Implementación del sistema de tipos de mi lenguaje en C++

Piense en lo siguiente: Ruby, Python, PHP y Co tienen un montón de built-in tipos que obviamente se implementan en C. Entonces, lo primero que intenté fue hacer posible dar un valor en mi idioma de tres posibles tipos: Int, String y Nil.

me ocurrió esto:

enum ValueType 
{ 
    Int, String, Nil 
}; 

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

Sí, wow, lo sé. Fue extremadamente lento pasar esta clase como el asignador de cadenas tuvo que ser llamado todo el tiempo.

La próxima vez que he intentado algo similar a esto:

enum ValueType 
{ 
    Int, String, Nil 
}; 

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

me habría almacenar todas las cadenas en stringTable y escribir su posición para index. Si el tipo de Value fuera Int, acabo de almacenar el número entero en index, no tendría sentido usar un índice int para acceder a otra int, o?

De todos modos, el anterior me dio un dolor de cabeza también. Después de un tiempo, acceder a la cadena de la tabla aquí, hacer referencia allí y copiarla allí creció sobre mi cabeza, perdí el control. Tuve que poner el borrador del intérprete abajo.

Ahora: Bien, entonces C y C++ están escritos estáticamente.

  • ¿Cómo las principales implementaciones de los idiomas mencionados anteriormente manejan los diferentes tipos en sus programas (fixnums, bignums, nums, cadenas, matrices, recursos, ...)?

  • ¿Qué debo hacer para obtener la máxima velocidad con muchos tipos diferentes disponibles?

  • ¿Cómo se comparan las soluciones con mis versiones simplificadas anteriores?

+0

"int, String, no procede", ¿qué pasa con flotador ? – hhafez

+1

¡Apoyo completamente la idea de un lenguaje de programación sin 'float'! De lo contrario, la primera pregunta sub_Language en SO será "Oye, ¿por qué no 0.1 + 0.2 == 0.3 en sub_Language? ¡Esta roto!". – bobince

+0

@sub: si eres tú el que está bajando algunas de las respuestas, deberías reconsiderarlo. Hay respuestas válidas que han sido votadas negativamente sin ninguna razón, algunas de las cuales podrían ser que usted no entendió la respuesta en toda su extensión. Si no ha sido usted, a quien lo hizo: por favor, explique lo que considera incorrecto de las respuestas. Esa es la única forma de mejorar realmente el sistema. –

Respuesta

4

Hay un par de cosas diferentes que puede hacer aquí. Diferentes soluciones han surgido en el tiempo, y la mayoría de ellas requieren una asignación dinámica del dato real (la variante boost :: puede evitar el uso de memoria asignada dinámicamente para objetos pequeños, gracias a @MSalters).

enfoque Pure C:

información de tipo de tienda y un puntero nulo a la memoria que ha de interpretarse de acuerdo con la información de tipo (por lo general una enumeración):

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

En C++ se puede mejorar esta enfoque mediante el uso de clases para simplificar el uso, pero lo más importante es que puede buscar soluciones más complejas y utilizar las bibliotecas existentes como boost :: any o boost :: variant que ofrecen diferentes soluciones para el mismo problema.

Both boost :: any y boost :: variant almacenan los valores en la memoria asignada dinámicamente, generalmente a través de un puntero a una clase virtual en una jerarquía, y con operadores que reinterpretan (down castts) a los tipos concretos.

+0

No creo que boost :: variant (o incluso boost :: any) _require_ dynamic allocation. Tienen trucos especiales para eludir eso cuando sea posible. En términos generales, a pesar de que tienen un 'void *' para objetos grandes en el montón, ese puntero puede ser parte de una unión cuyos otros miembros se usan para contener tipos de datos pequeños. – MSalters

+0

@MSalters: Correcto. No había examinado la implementación de la variante y sí utiliza un buffer que se reinterpreta según el tipo particular que se esté utilizando, como usted explicó. Boost any, por otro lado, es mucho más simple y usa memoria asignada dinámicamente incondicionalmente. –

1

En cuanto a la velocidad, que dicen:

era extremadamente lento para pasar esta clase su alrededor como el asignador de cadena tuvo que ser llamado todo el tiempo.

¿Sabía que debe pasar objetos por referencia la gran mayoría de las veces? Su solución parece viable para un simple intérprete.

+0

Eso no fue tan fácil la mayor parte del tiempo. Tuve que hacer varias copias ya que muchas funciones modifican los valores temporalmente. – sub

+1

@sub Bueno, eso suena dudoso. No veo por qué alguna vez cambiarías un valor, una vez que se creó, a menos que el usuario se lo esté asignando. –

4

Una solución obvia es definir una jerarquía de tipos:

class Type 
{ 
}; 

class Int : public Type 
{ 
}; 

class String : public Type 
{ 
}; 

y así sucesivamente.Como un ejemplo completo, escribamos un intérprete para un lenguaje minúsculo. El lenguaje permite declarar variables como esto:

var a 10 

que creará un objeto Int, asignarle el valor 10 y almacenarlo en la mesa de una variable con el nombre a. Las operaciones se pueden invocar en variables. Por ejemplo, la operación de suma de dos valores int se parece a:

+ a b 

Aquí está el código completo para el intérprete:

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

Una interacción de la muestra con el intérprete:

/home/me $ ./mylang 

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

¿Puedo almacenarlos todos en una sola matriz? ¿O son tipos completamente diferentes como string vs int? Soy relativamente nuevo en esto. – sub

+0

+1 @Vijay Mathew: Mis pensamientos exactamente – hhafez

+0

@Vijay: ¿Podría explicar cómo y dónde almacenar las instancias de las clases? – sub

1

C++ es un lenguaje fuertemente tipado. Veo que proviene de un lenguaje no tipado y sigo pensando en esos términos.

Si realmente necesita almacenar varios tipos en una variable, eche un vistazo a boost::any.

Sin embargo, si está implementando un intérprete, debe utilizar la herencia y las clases que representan un tipo específico.

+0

Ese no es mi problema. No me dijiste * cómo * y * dónde * almacenar las instancias de esas clases. – sub

+0

He votado a favor, ya que la respuesta es buena: use boost :: any. No veo cómo no es tu problema. –

+0

+1, de acuerdo. boost :: any es una solución a la pregunta escrita, incluso si no es una respuesta (completa) a la pregunta en la cabecera del sub. – MSalters

0

De acuerdo con la solución de Vijay la ejecución será:

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

El bit desaparecido de su código es cómo extraer esos valores ... Aquí está mi versión (en realidad lo aprendí de ogro y lo modificó para mi gusto).

uso es algo así como:

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, ahora a ver si un elemento particular es una cadena:

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

El código para el Cualquier clase es la siguiente. Siéntete libre de usarlo como quieras :). ¡Espero que esto ayude!

En el archivo de cabecera ... digamos 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; 
} 

Ahora en el archivo 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

Olvidé mencionar. En términos de velocidad, es efectivamente un puntero a la instancia clas de PlaceHolder, que almacena el puntero al valor que se transmite. No demasiado, supongo. Y es seguro para tipos y no necesita bibliotecas complejas como Boost. –

Cuestiones relacionadas