Boost.Variant

The Boost.Variant library provides an easy mechanism to write algorithms that process objects that can be one of several types without using object-orientated design and a potentially complex polymorphic inheritance. The mechanism consists of a container (boost::variant) that stores an object that has one of a number of pre-determined types and a visitor pattern that allows the user to specify how these objects should be processed depending on actual type that is encountered.

For example:

double sum(boost::variant<int,
                          const std::vector<int> &,
                          const std::list<int> & > v)
{
...
}

declares a function that returns either the parameter passed (in case it is of type int) or the sum of the elements of a sequence that is either a std::vector or a std::list.

The key features of this library are:

  • The actual type of object stored in the variant is determined at run-time, e.g.,:

    // ...
    boost::variant< std::vector<int> ,
                    std::list<int> > v;
    if (n > 10000)
       v=std::vector();
    else
       v=std::list();
    

    allows the use of either a vector or a list depending on the number of elements in the sequence which will only be known at run-time.

  • The type of the variant object is guaranteed to be one of the types supplied as parameters. For example, in:

    boost::variant<int, std::vector<int> > v;
    

    the object contained in v is guaranteed to be either of a type int or of the type std::vector<int>

  • Implementation uses no virtual calls and usually the objects are placed on the stack, ensuring high efficiency.

  • An object of boost::variant type is best processed using the visitor concept, for which the library defines a class boost::static_visitor<>. For example:

    class sumv :
      public boost::static_visitor<double>
    {
    public:
      template<class T>
      double operator()(const T &v) const
      {
        typename T::value_type t=0;
        for(typename T::const_iterator i(v.begin());
            i != v.end();
            ++i)
        {
          t+=*i;
        }
        return t;
      }
    };
    

    defines a visitor that returns the sum of a standard-library compliant container object.

  • Visitor classes are checked at compile-time to ensure that they can process all of the types present in the variant objects on which they are used.

Decision on when it is optimal to use the Boost.Variant library instead of alternatives is somewhat subjective and depends on, amongst other things, the anticipated future use of the code. However some useful rules-of-thumb can be provided:

  • If the type of object that will be stored can be determined at compile-time then you should not use variant but rather plain templates
  • If it is necessary to be able to add further possible types to be stored in the object after compilation and/or without modification of the code than you can not use variant and must use polymorphic inheritance -- this is what it is designed to do
  • Conversely, if a code is not part of the public interface and only a small number of possible types is required, use of variant is both concise and efficient

Here is a complete example showing run-time selection of container to be used in a program:

#include <vector>
#include <list>
#include <iostream>
#include <boost/variant.hpp>
#include <boost/lexical_cast.hpp>

class sumv :
  public boost::static_visitor<double>
{
public:
  template<class T>
  double operator()(const T &v) const
  {
    typename T::value_type t=0;
    for(typename T::const_iterator i(v.begin());
        i != v.end();
        ++i)
    {
      t+=*i;
    }
    return t;
  }
};


int main(int argc, char **argv)
{
  const size_t nelements = boost::lexical_cast<size_t>(argv[1]);

  boost::variant<std::vector<double>, std::list<double>  > v;

  if (nelements > 100)
  {
    v=std::vector<double>(nelements);
  }
  else
  {
    v=std::list<double>(nelements);
  }

  // Enter some elements into v according to application
  // ....
  //

  std::cout<<boost::apply_visitor( sumv(), v )
           <<std::endl;
}