C++ Boost

boost::variant

Reference



Header "boost/variant.hpp"

Synopsis

Dependencies and library features defined in "boost/variant.hpp":

#include <typeinfo>

#define BOOST_VARIANT_LIMIT_TYPES implementation-defined

namespace boost
{
    template
    <
        typename T1,
        typename T2,
        ...
        typename Tn = implementation-defined
    >
    class variant;
    
    template <typename T1, typename T2, ... , typename Tn>
    void swap(
          variant<T1, T2, ... , Tn> &
        , variant<T1, T2, ... , Tn> &
        );
}

Test harnesses provided in "libs/variant/test" directory.


BoundedType Concept

Given variant<T1, T2, ... , Tn>, each type Ti is a bounded type of the variant.

The requirements on bounded types are as follows:

A variant's first bounded type has the additional requirement that it is DefaultConstructible [??].


Visitor Concept

Given variant<T1, T2, ... , Tn>, a function object which unambiguously accepts any value of each of the variant's bounded types, is a Visitor of of the variant.

Additional requirements on visitors are as follows:

Examples:

The following class is a visitor of a number of variant types (e.g., explicitly: variant<int, std::string>, or implicitly: variant<short, std::string>, etc.):

class my_visitor
{
    typedef int result_type;

    int operator()(int i)
    {
        return i * 2;
    }

    int operator()(std::string& s)
    {
        return s.length();
    }
};

Another example is the following class, which exposes a templated function operator, allowing it to operate on values of many types. Thus, the following class is a visitor of any variant whose bounded types each support streaming output:

class printer
{
    typedef void result_type;

    template <typename T>
      void operator()(const T& t)
    {
        std::cout << t << '\n';
    }
};

BOOST_VARIANT_LIMIT_TYPES

#define BOOST_VARIANT_LIMIT_TYPES implementation-defined

Implementation-defined. Equal to the length of the template parameter list for variant.

Note: Conforming implementations of variant must allow at least ten bounded types. That is, BOOST_VARIANT_LIMIT_TYPES >= 10 must evaluate true.


variant

template
<
    typename T1,
    typename T2,
    ...
    typename Tn = implementation-defined
>
class variant
{
public: // structors

    variant();
    variant(const variant &);
    template <typename OperandType>
      variant(const OperandType &);
    ~variant();

public: // modifiers

    variant & swap(variant &);
    variant & operator=(const variant &);
    template <typename OperandType>
      variant & operator=(const OperandType &);

public: // queries

    int which() const;
    const std::type_info & type() const;

    bool empty() const; // always false (boost::any compatibility)

private: // representation
    ...
};

An instance of variant contains exactly one instance of one of its bounded types, which are specified as arguments to variant's template parameter list. The length of variant's template parameter list is equal to the implementation defined value BOOST_VARIANT_LIMIT_TYPES.

Each type specified as a bounded type must satisfy the BoundedType requirements. Note that  variant itself satisfies BoundedType requirements with default construction.

All members of variant satisfy the strong guarantee of exception-safety, unless otherwise specified.


Structors

variant();

Default constructor. Initializes *this with the default value of the first bounded type (i.e, T1). May fail with any exceptions arising from the default constructor of T1.

variant(const variant& other);

Copy constructor. Copies the content of other into *this. May fail with any exceptions arising from the copy constructor of other's contained type.

template <typename OperandType>
  variant(const OperandType & operand);

Templated constructor. Initializes *this according to the following logic:

  1. If OperandType is not a variant:
    • If OperandType is one of the bounded types of the variant, initialize *this with a copy of operand.
    • Otherwise, use overload resolution rules to find the best conversion for OperandType, and initialize *this with a copy of the converted operand. (However, if the conversion is ambiguous, or if none exists, a compiler error is generated.)
  2. Otherwise (i.e: OperandType is a variant):
    • If OperandType does not appear on *this's set of types, then *this is initialized with operand's held value (as described in item 1, above).
    • Otherwise, operand is assigned, as-is, into *this. Hence, the held value of *this is, by itself, a variant.

May fail with any exceptions arising from the copy constructor of OperandType.

~variant();

Non-throwing destructor that releases all resources used in management of *this, including the currently contained value.

Modifiers

void swap(variant& other);

Exchanges contents of *this and other. May fail with any exceptions arising from the copy constructors of the contained types of *this or other, or from the swap primitive of the held values, if this->type() == other.type().

variant& operator=(const variant& rhs);

Copy assignment. Assigns rhs's contained value into *this. The old value contained by *this is properly destroyed.

Note: this operator follows the same logic as the copy constructor.

template<class OperandType>
  variant& operator=(const OperandType &);

Templated assignment. Assigns rhs into *this. The old value held by *this is properly destroyed.

Note: This operator follows the same logic as the templated constructor.

Queries

const std::type_info & type() const;

Non-throwing query that returns the typeid() of the contained value

bool empty() const;

Always returns false. This non-throwing member function is provided for boost::any compatibility.

Note: a variant object is never empty. (See the default constructor.)

int which() const;

Non-throwing query that returns the zero-based index of the bounded type of the contained value.


Visitation: apply_visitor

// Binary form
template<typename VisitorType, typename VariantType>
typename VisitorType::result_type apply_visitor(VisitorType& visitor,
   VariantType& var_inst);

// Unary form
template<class VisitorType>
boost::apply_visitor_t<VisitorType> apply_visitor(VisitorType& visitor);

template <typename VisitorType>
class apply_visitor_t
{
public:
    typedef typename VisitorType::result_type result_type;

    template <typename VariantType>
    result_type operator()(VariantType& var_inst);

    ...
};

boost::apply_visitor(visitor, var_inst) passes the variant object, var_inst, to the given visitor (visitor). This is equivalent to calling visitor's function-call operator, with var_inst's currently held value.
VisitorType must be a visitor of VariantType. See Functor-based visitation for an in-depth description of visitors.

The unary form of apply_visitor() tranforms the given visitor into a unary function object which accepts a variant object, thus, the following two lines are equivalent:

      boost::apply_visitor(visitor, var_inst); // Binary form
      boost::apply_visitor(visitor)(var_inst); // Unary form

Consequently, the unary apply_visitor() function, is highly useful when std::for_each (or a similar STL algorithm) needs to be applied on a sequence of variant objects, as illustrated in the Polymorphism: Inheritance Vs. Variants sample.


Visitation: static_visitor

template<typename R = void>
struct static_visitor
{
    typedef R result_type;
};

static_visitor defines the nested type result_type, which is required from each visitor class.


Value Extraction: extract


        
class bad_extract : public std::exception
{
public:

    virtual const char* what() const throw();

};

template <typename ToType>
struct extract
{
public: // typedefs

    typedef ToType& result_type;

public: // structors

    template <typename VariantType>
      extract(VariantType &);
    template <typename VariantType>
      extract(const VariantType &);

public: // queries

    bool check() const;
    ToType& operator()() const;
    operator ToType&() const; //  Not supported on MSVC

};

        

boost::extract is a facility for extracting a reference to a value held by a variant object. The 'extraction' succeeds only if the type of the held value is identical to the ToType template parameter. Usage:

  1. extract<RR>(var_inst)(); Initializes a temporary extractobject and converts it to RR&. If the given variant object, var_inst does not hold a value of type RR, a bad_extract exception is thrown.
  2. extract<RR> ex(var_inst); Initalizes an extract object, ex. Subsequently, client code can issue an ex.check() call, to determine whether var_inst is holding a value of type RR.

Constructors

    template<VariantType>
    extract(const VariantType& from);

    template<VariantType>
    extract(VariantType& from);

Sets up an extract object which is associated with the given variant object (from).

Queries

    bool check() const;   

check() is a non throwing member function which return true, if, and only if, the associated variant object is holding a value of type ToType.

    result_type operator()() const; 
    operator result_type() const; //  Not supported on MSVC

If check() is true - operator() returns a reference to the value held by the associated variant object. Otherwise - a bad_extract exception is thrown.
operator result_type() supplies an implicit conversion to ToType&. It is semantically identical to operator(). Note that the MSVC7 implementation does not support this operator.


incomplete

incomplete<T> is a template class, which allows a variant type to be instantiated with incomplete types.
By specifying incomplete<T> as one of the actual template parameters, the instantiated variant will be able to handle values of type T, although T is incomplete at the instantiation point.
incomplete<> is typically used for solving circular dependencies, but, more importantly, it also enables the creation of recursive, variant-based, constructs.
The snip below demonstrates the usage of Incomplete<>. A complete sample program is available here.

   using boost::variant;
   using boost::incomplete;

   struct non_leaf_node; // Forward declaration

   // Define a tree_node variant with these two types:
   //   (1) int, (2) non_leaf node
   typedef variant
   <
      int,
      incomplete<non_leaf_node>  // non_leaf_node is incomplete at
                                 // this point so it must be wrapperd
                                 // by incomplete<>
   > tree_node;

   struct non_leaf_node
   {
      non_leaf_node(const non_leaf_node& other)
         :   left_(other.left_), right_(other.right_), num_(other.num_)
      { }

      int num_;
      tree_node left_;
      tree_node right_;
   };


Revised 14 February 2003

© Copyright Eric Friedman and Itay Maman 2002-2003. All rights reserved.

Permission to use, copy, modify, distribute and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation. Eric Friedman and Itay Maman make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty.