C++在成员变量中保存不同类型数据的优雅方式

Ggi*_*cci 1 c++ boost c++11

如果我有一个类,其数据的类型可以是int,float,double,char[],std::string,std::vector...现在我使用的是enum指示哪些类型的数据和void*动态的数据分配内存.但是,我确信必须有一个更优雅的方式.如何实现它而不使用boost

Ang*_*llo 5

实现"Variant"或"Any"类型,正如其他人指出的那样,您可以使用一些实现.但是如果你不想使用boost或其他替代品,你可以实现自己的简单版本.

您将需要2个类型的结构,一个将存储的基类,以及一个将保存实际对象的派生模板类.

让我们称之为占位符和持有者:

这是基础结构:

/**
 * @brief The place holder structure..
 */
struct PlaceHolder
{
  /**
   * @brief Finalizes and instance of the PlaceHolder class.
   */
    virtual ~PlaceHolder() {}

    /**
     * @brief Gets the type of the underlying value.
     */
    virtual const std::type_info& getType() const = 0;

    /**
     * @brief Clones the holder.
     */
    virtual PlaceHolder * clone() const = 0;
};
Run Code Online (Sandbox Code Playgroud)

这将是派生类:

template<typename ValueType>
struct Holder: public PlaceHolder
{

    /**
     * @brief Initializes a new instance of the Holder class.
     *
     * @param ValueType The value to be holded.
     */
    Holder(const ValueType & value) : held(value) {}

    /**
     * @brief Gets the type of the underlying value.
     */
    virtual const std::type_info & getType() const
    {
      return typeid(ValueType);
    }

    /**
     * @brief Clones the holder.
     */
    virtual PlaceHolder * clone() const
    {
      return new Holder(held);
    }

    ValueType held;
};
Run Code Online (Sandbox Code Playgroud)

现在我们可以这样:

PlaceHolder* any = new Holder<int>(3);
Run Code Online (Sandbox Code Playgroud)

我们可以像这样从中获取价值:

int number = static_cast<Holder<int> *>(any)->held;
Run Code Online (Sandbox Code Playgroud)

这不是很实用,所以我们创建了一个类来处理所有这些东西,并添加一些comodities,让我们称之为Any:

/**
 * @brief This data type can be used to represent any other data type (for example, integer, floating-point,
 * single- and double-precision, user defined types, etc.).
 *
 * While the use of not explicitly declared variants such as this is not recommended, they can be of use when the needed
 * data type can only be known at runtime, when the data type is expected to vary, or when optional parameters
 * and parameter arrays are desired.
 */
class Any
{
  public:

    /**
     * @brief Initializes a new instance of the Any class.
     */
    Any()
    : m_content(0)
    {
    }

    /**
     * @brief Initializes a new instance of the Any class.
     *
     * @param value The value to be holded.
     */
    template<typename ValueType>
    Any(const ValueType & value)
    : m_content(new Holder<ValueType>(value))
    {
    }

    /**
     * @brief Initializes a new instance of the Any class.
     *
     * @param other The Any object to copy.
     */
    Any(const Any & other)
    : m_content(other.m_content ? other.m_content->clone() : 0)
    {
    }

    /**
     * @brief Finalizes and instance of the Any class.
     */
    virtual ~Any()
    {
      delete m_content;
    }

    /**
     * @brief Exchange values of two objects.
     *
     * @param rhs The Any object to be swapped with.
     *
     * @return A reference to this.
     */
    Any& swap(Any & rhs)
    {
      std::swap(m_content, rhs.m_content);

      return *this;
    }

    /**
     * @brief The assignment operator.
     *
     * @param rhs The value to be assigned.
     *
     * @return A reference to this.
     */
    template<typename ValueType>
    Any& operator=(const ValueType & rhs)
    {
      Any(rhs).swap(*this);
      return *this;
    }

    /**
     * @brief The assignment operator.
     *
     * @param rhs The value to be assigned.
     *
     * @return A reference to this.
     */
    Any & operator=(const Any & rhs)
    {
      Any(rhs).swap(*this);

      return *this;
    }

    /**
     * @brief The () operator.
     *
     * @return The holded value.
     */
    template<typename ValueType>
    ValueType operator()() const
    {
      if (!m_content)
      {
        //TODO: throw
      }
      else if (getType() == typeid(ValueType))
      {
        return static_cast<Any::Holder<ValueType> *>(m_content)->held;
      }
      else
      {
        //TODO: throw
      }
   }

    /**
     * @brief Gets the underlying value.
     *
     * @return The holded value.
     */
    template<typename ValueType>
    ValueType get(void) const
    {
      if (!m_content)
      {
        //TODO: throw
      }
      else if (getType() == typeid(ValueType))
      {
        return static_cast<Any::Holder<ValueType> *>(m_content)->held;
      }
      else
      {
        //TODO:  throw
      }
    }   

    /**
     * @brief Tells whether the holder is empty or not.
     *
     * @return <tt>true</tt> if the holder is empty; otherwise <tt>false</tt>.
     */
    bool isEmpty() const;
    {
      return !m_content;
    }

    /**
     * @brief Gets the type of the underlying value.
     */
    const std::type_info& getType() const;
    {
      return m_content ? m_content->getType() : typeid(void);
    }

  protected:

    /**
     * @brief The place holder structure..
     */
    struct PlaceHolder
    {
      /**
       * @brief Finalizes and instance of the PlaceHolder class.
       */
        virtual ~PlaceHolder() {}

        /**
         * @brief Gets the type of the underlying value.
         */
        virtual const std::type_info& getType() const = 0;

        /**
         * @brief Clones the holder.
         */
        virtual PlaceHolder * clone() const = 0;
    };

    template<typename ValueType>
    struct Holder: public PlaceHolder
    {

        /**
         * @brief Initializes a new instance of the Holder class.
         *
         * @param ValueType The value to be holded.
         */
        Holder(const ValueType & value) : held(value) {}

        /**
         * @brief Gets the type of the underlying value.
         */
        virtual const std::type_info & getType() const
        {
          return typeid(ValueType);
        }

        /**
         * @brief Clones the holder.
         */
        virtual PlaceHolder * clone() const
        {
          return new Holder(held);
        }

        ValueType held;
    };

  protected:

    PlaceHolder* m_content;
};
Run Code Online (Sandbox Code Playgroud)

此实现基于Any of Ogre

你可以使用它,例如:

int main()
{
  Any three = 3;

  int number = three.get<int>();

  cout << number << "\n";

  three = string("Three");

  std::string word = three.get<string>();

  cout << word << "\n";

  return 0;
}
Run Code Online (Sandbox Code Playgroud)

输出:

3
Three
Run Code Online (Sandbox Code Playgroud)