Click here to Skip to main content
13,000,127 members (51,453 online)
Click here to Skip to main content
Add your own
alternative version


26 bookmarked
Posted 23 Feb 2004

Tiny Template Library: variant

, 23 Feb 2004
Rate this:
Please Sign up or sign in to vote.
An article on how to implement and use variant<>. Variant is useful for creating heterogeneous containers and much more.
<!------------------------------- STEP 3 ---------------------------><!-- Add the article text. Please use simple formatting (


etc) -->


My standard warning: Don't try to compile this project with MSVC v6.0/v7.0. This project requires a compliant compiler. MSVC v7.1 or GCC v3.2.3 will work just fine. In the article about typelist, I briefly mentioned variant. Here, I'd like to discuss it in more detail. The code in this article is to demonstrate the basic ideas only. The actual implementation can be found in TTL. In C++, it is not legal to have non-POD data types in union. For example the following code won't compile.

struct my_type
   int x;
   my_type() : x(0) {}
   virtual ~my_type();

union my_union
   my_type a;
   double b;

The variant template solves this problem and adds a lot of other cool features. One application of variant is heterogeneous containers.

typedef variant< my_type, double > mv;

  my_type a;
  std::vector< mv > v;
  v.push_back(2.3); //add double 
  v.push_back(a); //add my_type

The variant semantic was inspired by boost::variant. A very good discussion about variant can be found in "Modern C++ Design" by A. Alexandrescu.


variant has a variable number of template parameters.


variant<int, double>

To support variable numbers of template parameters, we use the technique that was suggested in the typelist article.

The main variant implementation ideas are:

  • Compile-time: convert variant template parameters to typelist.
  • Compile-time: using the typelist, find the largest element and reserve a buffer of this element size.
  • Run-time: use the reserved buffer to construct controlled objects in place.
  • Run-time: keep the index of the current instance type.
  • Run-time: if not initialized, variant is in a singular state.

    The variant pseudo-code looks like this:

    template < typename T1, typename T2, ... > 
    struct variant
        //list of user types
        typedef meta::typelist< T1, T2,...> types;
        variant() : p_(0) {}
        template< typename T >
        variant( const T& d ) : p_(0) 
            //find the index of this type in the variant typelist
            which_ = find_type<T>::value;
            //in place construction
            p_ = new(buffer_) data_holder<T>(d);
        virtual ~variant() { destroy(); }
        inline int which() const { return which_; }
        inline bool is_valid() const { return p_ != 0; }
        void destroy() 
            if(!is_valid()) return;
            p_ = 0;
        //data_holder is a wrapper for the user types
        struct data_holder_base 
            virtual ~data_holder_base() {}
        temlate< typename T >
        struct data_holder : data_holder_base
            T d_;
            data_holder( const T& d ) : d_(d) {}
        //list of data holder types
        typedef meta::typelist<data_holder<T1>, data_holder<T2>,...> holder_types;
        //reserve enough space to hold the largest type
        char buffer_[find_largest_type<holder_types>::value];
        //pointer to the controlled object
        data_holder_base *p_;
        //object type index
        int which_;
    Please note: the above code is only a pseudo-code. The actual implementation is more complex. It might take a small book to describe all the details. I'd rather talk about how to use variant in practice.

    Using variant

    Let's consider a simple example:
    typedef variant<int, double> my_variant;
    This typedef defines a data type that can contain a double or int variable. Suppose that we need to write a function that does something with my_variant depending on the variable data type. We can use a simple switch/case statement.
    void f( my_variant& v )
        int n;
        double x;
        switch( v.which() )
        case 0:  //int variable
            n = get<int>(v);
            //do something with int;
        case 1:  //double variable
            x = get<double>(v);
            //do something with the double;

    As you can see, the get<> function can be used to retrieve the typed data from variant<>. Obviously this switch statement is ugly and not very flexible. The function f() has to know the type indexes in my_variant. One way to solve these problems is to utilize the Gof visitor pattern ideas.

  • Define a variant visitor functor that has a separate operator() for all types in variant.
  • When applied to the variant, an appropriate visitor's operator() is called.

    TTL's variant has the apply_visitor<> function that takes care of calling the appropriate visitor operator(). Using this technique, the above example can be implemented as follows.
    typedef variant<int, double> my_variant;
    struct visitor
        void operator()(int n)
        //do something with the int;
        void operator()(double x)
        //do something with the double;
        //ignore any other types
        template< typename T >
        void operator()( T d )
    my_variant var;
    visitor vis;
    apply_visitor(var, vis);

    I think that it looks much nicer and we don't have to worry about type indexes or any other type identifiers for the same matter. The apply_visitor() function is implemented in TTL. apply_visitor does the following:

  • finds what type is identified by the which_ member;
  • casts the object's pointer to this type;
  • passes the casted pointer to the user supplied visitor.
  • the compiler automatically selects the appropriate operator().

    Another interesting implementation of variant is event dispatching. Suppose we have an event source that can generate multiple event types. For the simplicity sake, the event types are int and double.  We can define the event type as following:

    typedef variant< int, double > event;
    Now we need a way to specify a callback function that will be called by the event source to notify the client or observer. It is convenient to define callbacks using generic functors (see, TTL:implementing functors)

    typedef function< void (event&) > callback;
    Now we can put everything together:
    typedef variant< int, double > event;
    typedef function< void (event&) > callback;
    struct event_source
        callback cb_
        event_source( callback& cb ) : cb_(cb) {}
        void do_something()
            event ev;
            //generate int event
            ev = 1;
            cb_( ev );
            //generate double event
            ev = 2.3;
            cb_( ev );
    //define our event vistor
    struct event_visitor
        //process int event
        void operator()(int n)
            cout << "got int:" << n;
        //process double event
        void operator()(double n)
            cout << "got double:" << n;
        //ignore any other events
        template< typename T >
        void operator()( T d )
    void my_callback( event& e )
        event_visitor vistor;
        apply_visitor(e, vistor);
        event_source src(my_callback);
    You can find a working example in the samples/test folder. It is not hard to extend this example to a complete Observer pattern implementation w/o any polymorphic inheritances. As a result, a "strong" type checking is performed at compile time.
  • License

    This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

    A list of licenses authors might use can be found here


    About the Author

    Software Developer (Senior)
    United States United States
    No Biography provided

    You may also be interested in...

    Comments and Discussions

    GeneralPerformance and Details Pin
    tpolzin25-Aug-04 4:27
    membertpolzin25-Aug-04 4:27 
    GeneralRe: Performance and Details Pin
    kig25-Aug-04 19:02
    memberkig25-Aug-04 19:02 
    Cool! Have you tried to profile it?

    > Do you have an explaination for this?

    Perhaps compilers optimize switch/case statements
    much better than if/else chains.
    If it is the case then there is no surprise.
    boost::pool is basically constructing objects
    in a reserved space just like variant does it
    while there is no if/else chains.
    I think that it is quite possible to change
    variant to switch/case from if/else.
    The type_switch code in typlist.h won't be
    that elegant but it is possible.

    How do you results depend on number of
    different event types if at all?

    Another reason could be all this overhead
    associated with accessing data and constructing
    temporary objects on the stack in variant.

    > Concerning your article, I have some probably silly questions:

    No, they are not silly on the contrary.

    > 1. What is the reasoning for making ~variant "virtual"?

    I guess if you want variant to be a base class.
    However I agree that it may not be a compelling reason.

    > 2. What does "p_->~data_holder_base();" do, as there is no destructor defined in data_holder_base or any data_holder?

    Please download the latest version from sourceforge
    It doesn't call ~data_holder_base() any more.
    data_holder_base used to have a virtual destructor.
    However I still have to call p->~data_holder< T >();
    variant constructs data_holder objects in-place in a reserved buffer that is equal to the size
    of the largest data_holder type. data_holder constructs and stores user defined objects.
    I use data_holder (to keep the user data) instead of the user types directly for several reasons,
    one of which is the alignment.
    template< typename T >;
    struct data_holder { T d_; };
    sizeof(data_holder< T >) could be very different from sizeof(T) depending on the T alignment.
    By calling p_->~data_holder() I am destroying the user object that is stored
    in the data_holder.
    Does it make sense?

    > 3. In the description of "apply_visitor": "the compiler automatically selects the appropriate operator()".
    > Did I get it right that in the case the compiler cannot detect the current type at runtime

    You are absolutely correct. I meant that compiler creates associations between
    operator() overloads and variant types.

    > Using typelists and inlining the compiler generates automatically an if-then-else chain: "if (which_ == 1 ) visitor(

    get<type1>(v)); else if (which_ == 2 ) ..."

    Correct. The magic happens in type_switch (typlist.hpp).

    I think that the biggest performance difference between
    boost::variant and ttl::variant happens in assignments
    in certain cases.

    If a boost::variant type is controlling only
    non-POD types (that might throw), it'll allocate
    a default constructible object in the memory heap
    every time you do an assignment. It is done
    to provide basic exception safety guarantees
    because boost::variant's semantic
    doesn't allow singular (when the variant
    content is undefined) variant states.
    ttl::variant allows singular states. So it never
    uses memory heap.
    Does it make sense?

    GeneralRe: Performance and Details Pin
    tpolzin25-Aug-04 23:26
    membertpolzin25-Aug-04 23:26 
    GeneralRe: Performance and Details Pin
    kig26-Aug-04 18:16
    memberkig26-Aug-04 18:16 
    GeneralRe: Performance and Details Pin
    tpolzin26-Aug-04 22:58
    membertpolzin26-Aug-04 22:58 
    GeneralRe: Performance and Details Pin
    kig1-Sep-04 20:50
    memberkig1-Sep-04 20:50 
    GeneralRe: Performance and Details Pin
    tpolzin2-Sep-04 1:15
    membertpolzin2-Sep-04 1:15 
    GeneralRe: Performance and Details Pin
    kig6-Sep-04 15:45
    memberkig6-Sep-04 15:45 
    QuestionWhat's the difference to Boost.Variant? Pin
    Hartmut Kaiser25-Feb-04 21:47
    memberHartmut Kaiser25-Feb-04 21:47 
    AnswerRe: What's the difference to Boost.Variant? Pin
    kig26-Feb-04 7:01
    memberkig26-Feb-04 7:01 
    GeneralRe: What's the difference to Boost.Variant? Pin
    Hartmut Kaiser26-Feb-04 9:08
    memberHartmut Kaiser26-Feb-04 9:08 
    GeneralRe: What's the difference to Boost.Variant? Pin
    kig26-Feb-04 11:28
    memberkig26-Feb-04 11:28 
    AnswerRe: What's the difference to Boost.Variant? Pin
    kig26-Feb-04 11:49
    memberkig26-Feb-04 11:49 
    GeneralRe: What's the difference to Boost.Variant? Pin
    Hartmut Kaiser26-Feb-04 19:40
    memberHartmut Kaiser26-Feb-04 19:40 

    General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

    Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

    Permalink | Advertise | Privacy | Terms of Use | Mobile
    Web02 | 2.8.170624.1 | Last Updated 24 Feb 2004
    Article Copyright 2004 by kig
    Everything else Copyright © CodeProject, 1999-2017
    Layout: fixed | fluid