Boost C++ Libraries Home Libraries People FAQ More

PrevUpHomeNext

Chapter 20. Boost.Variant

Eric Friedman

Itay Maman

Permission to copy, use, sell and distribute this software is granted provided this copyright notice appears in all copies. Permission to modify the code and to distribute modified code is granted provided this copyright notice appears in all copies, and a notice that the code was modified is included with the copyright notice.

This software is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.

Table of Contents

Introduction
Abstract
Motivation
Tutorial
Basic Usage
Advanced Topics
Reference
Concepts
Header <boost/variant.hpp>
Header <boost/variant/variant_fwd.hpp>
Header <boost/variant/variant.hpp>
Header <boost/variant/recursive_variant.hpp>
Header <boost/variant/recursive_wrapper.hpp>
Header <boost/variant/apply_visitor.hpp>
Header <boost/variant/get.hpp>
Header <boost/variant/bad_visit.hpp>
Header <boost/variant/static_visitor.hpp>
Header <boost/variant/visitor_ptr.hpp>
Design Overview
"Never-Empty" Guarantee
Miscellaneous Notes
Boost.Variant vs. Boost.Any
Portability
Troubleshooting
Acknowledgments
References

Introduction

Abstract

The variant class template is a safe, generic, stack-based discriminated union container, offering a simple solution for manipulating an object from a heterogeneous set of types in a uniform manner. Whereas standard containers such as std::vector may be thought of as "multi-value, single type," variant is "multi-type, single value."

Notable features of boost::variant include:

Motivation

Problem

Many times, during the development of a C++ program, the programmer finds himself in need of manipulating several distinct types in a uniform manner. Indeed, C++ features direct language support for such types through its union keyword:

union { int i; double d; } u;
u.d = 3.14;
u.i = 3; // overwrites u.d (OK: u.d is a POD type)

C++'s union construct, however, is nearly useless in an object-oriented environment. The construct entered the language primarily as a means for preserving compatibility with C, which supports only POD (Plain Old Data) types, and so does not accept types exhibiting non-trivial construction or destruction:

union {
  int i;
  std::string s; // illegal: std::string is not a POD type!
} u;

Clearly another approach is required. Typical solutions feature the dynamic-allocation of objects, which are subsequently manipulated through a common base type (often a virtual base class [Hen01] or, more dangerously, a void*). Objects of concrete type may be then retrieved by way of a polymorphic downcast construct (e.g., dynamic_cast, boost::any_cast, etc.).

However, solutions of this sort are highly error-prone, due to the following:

  • Downcast errors cannot be detected at compile-time. Thus, incorrect usage of downcast constructs will lead to bugs detectable only at run-time.
  • Addition of new concrete types may be ignored. If a new concrete type is added to the hierarchy, existing downcast code will continue to work as-is, wholly ignoring the new type. Consequently, the programmer must manually locate and modify code at numerous locations, which often results in run-time errors that are difficult to find.

Furthermore, even when properly implemented, these solutions tend to incur a relatively significant abstraction penalty due to the use of the heap, virtual function calls, and polymorphic downcasts.

Solution: A Motivating Example

The boost::variant class template addresses these issues in a safe, straightforward, and efficient manner. The following example demonstrates how the class can be used:

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

class my_visitor : public boost::static_visitor<int>
{
public:
    int operator()(int i) const
    {
        return i;
    }
    
    int operator()(const std::string & str) const
    {
        return str.length();
    }
};

int main()
{
    boost::variant< int, std::string > u("hello world");
    std::cout << u; // output: hello world

    int result = boost::apply_visitor( my_visitor(), u );
    std::cout << result; // output: 11 (i.e., length of "hello world")
}

PrevUpHomeNext