Boost C++ Libraries Home Libraries People FAQ More

PrevUpHomeNext

Chapter 34. Boost.Variant

Eric Friedman

Itay Maman

Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

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