How to implement a boost::variant derived-class?

佐手、 提交于 2019-12-05 04:16:51

The accepted answer and the other answers do not answer the question. There are good reasons to derive from boost::variant.

  • you want to add methods into the variant, so that it behaves more polymorphic
  • you want to keep using boost::apply_visitor(visitor(), variant) without jumping through hoops

Here is a working example of a type MyVariant inheriting from boost::variant. The code requires C++11 to inherit the (complex) constructors of boost::variant.

Edit: Operators such as equal comparison implemented in boost::variant do not automatically work for the derived class. This is a (unintended) consequence of how boost::variant is implemented, it explicitly prevents comparisons to "foreign" types using templates. Operators can be enabled with explicit overloads, as shown in the example.

#include<iostream>
#include<boost/variant.hpp>

struct type_size_visitor : public boost::static_visitor<unsigned> {
  template <typename T>
  unsigned operator()(const T&) const { return sizeof(T); }
};

template <typename... Types>
class MyVariant : public boost::variant<Types...>
{
  using base_type = boost::variant<Types...>;
public:
  // inherit constructors
  using base_type::base_type;

  // add a new method
  unsigned type_size() { return boost::apply_visitor(type_size_visitor(), *this); }

  /* Inheriting operators from boost::variant does not
     work as it normally would. We have to add explicit
     overloads for the operators we want to use.
  */
  bool operator==(const MyVariant& rhs) const {
    // cast rhs to base and delegate to operator in base
    return base_type::operator==(static_cast<const base_type&>(rhs));
  }
};

int main() {
  MyVariant<std::string, char> v;
  v = 1;
  std::cout << v.type_size() << " " << sizeof(char) << std::endl;
  v = std::string("foo");
  std::cout << v.type_size() << " " << sizeof(std::string) << std::endl;

  // comparison operators need workaround shown above
  MyVariant<std::string, char> a, b;
  a = 1; b = 1;
  assert(a == b);
}

I want an empty state

boost::variant<boost::blank, bool, long, std::string>

There. That was much easier. No need for messy inheritance.

I want to give enum types

enum Type
{
  NONE,
  BOOL,
  LONG,
  STRING
};

struct GetType : public boost::static_visitor<Type>
{
  Type operator()(boost::blank) {return NONE;}
  Type operator()(bool) {return BOOL;}
  Type operator()(long) {return LONG;}
  Type operator()(const std::string&) {return STRING;}
};

//Get the type
Type t = boost::apply_visitor(GetType(), theData);

That was easy too. Plus, if you add a new type to the variant, your code will break if you don't update GetType to match.

The other two criteria require you to use a class, but you don't need inheritance. You need containment.

typedef boost::variant<boost::blank, std::string, long> VarType;

class MyVariant
{
public:
    //Default construction will initialize with boost::blank.
    MyVariant(char c) : m_var(std::string(1,c)) {}
    MyVariant(const char* s) : m_var(std::string(s)) {}
    MyVariant(int v) : m_var(long(v)) {}
    MyVariant(long v) : m_var(long(v)) {}

    VarType &operator *() {return m_var;}
    const VarType &operator *() const {return m_var;}

private:
    VarType m_var;
};

...

Type t = boost::apply_visitor(GetType(), *theData);

Finally my implementation based on Nicol Bolas's answer:

Variant.h

class Variant 
{
  public:
  Variant()                     : v_(boost::blank()) {}
  Variant(bool     v)           : v_(v)              {}
  Variant(long     v)           : v_(v)              {}
  Variant(int      v)           : v_(long(v))        {}
  Variant(double   v)           : v_(v)              {}
  Variant(const std::string& s) : v_(s)              {}
  Variant(const char*        s) : v_(std::string(s)) {}

  typedef boost::variant <boost::blank, bool, 
                          long, double, std::string> bstvar;
  enum Type {                      
    NONE, BOOL,          // above underlying types
    LONG, DOUBLE, STRING // and enum must be consistent:
  };                     // (order must be same)

  operator          bstvar&  ()       { return v_; }
  operator    const bstvar&  () const { return v_; }
  bstvar      &     operator*()       { return v_; }
  bstvar const&     operator*() const { return v_; }
  bool              empty    () const { return  type() == NONE;          }
  bool              toBool   () const { return  boost::get<bool  > (v_); }
  long              toLong   () const { return  boost::get<long  > (v_); }
  double            toDouble () const { return  boost::get<double> (v_); }
  std::string const& toStr   () const { return  boost::get<std::string> (v_); }
  std::string      & toStr   ()       { return  boost::get<std::string> (v_); }
  inline Type        type    () const { return  (Type) v_.which();       }
  static Type        type    (const std::string&);
  static std::string type    (Type t);

  private:  bstvar v_;  // Data
};

Variant.cpp

#include "Variant.h"
#include <sstream>
#include <algorithm> //std::transform

Variant::Type   Variant::type   (const std::string& str)
{
  std::string lower = str;
  std::transform (lower.begin(), lower.end(), lower.begin(), ::tolower);

  if (lower == "bool")     return BOOL;
  if (lower == "long")     return LONG;
  if (lower == "double")   return DOUBLE;
  if (lower == "string")   return STRING;
  else                     return NONE;
}

std::string  Variant::type  (Type t)
{
  switch (t)
  {
    case NONE:    return "none";
    case BOOL:    return "bool";
    case LONG:    return "long";
    case DOUBLE:  return "double";
    case STRING:  return "string";

    default:
      ;//see below
  }
  std::ostringstream oss;
  oss <<"Unexpected type="<< t;
  return oss.str();
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!