Unions and std::variant

How can I define a recursive variant type?

Is it possible to have a variant that contains a type that itself contains the same variant type? How would I define such a recursive variant?

Illustration representing computer hardware

Yes, it is possible to define a variant that contains a type that itself contains the same variant type. This is known as a recursive variant. However, because variants must have a fixed size, you can't directly include a variant as one of its own alternative types.

Instead, you need to wrap the recursive variant in a type that allows for dynamic allocation, such as std::unique_ptr or std::vector.

Here's an example of a recursive variant that represents a simple expression tree:

#include <memory>
#include <string>
#include <variant>

struct Expr;
using ExprPtr = std::unique_ptr<Expr>;

struct Expr {
  std::variant<
    int, // integer literal
    std::string, // variable name

    // unary operation
    std::pair<char, ExprPtr>,

    // binary operation
    std::tuple<ExprPtr, char, ExprPtr>
  > value;
};

In this example, Expr is a struct that contains a variant value. The variant can hold an integer literal, a variable name (string), a unary operation (a character and a pointer to another Expr), or a binary operation (two pointers to Exprs and a character).

Here's how you could use this Expr type:

Expr makeIntegral(int i) {
  Expr e;
  e.value = i;
  return e;
}

Expr makeVariable(std::string name) {
  Expr e;
  e.value = std::move(name);
  return e;
}

Expr makeUnary(char op, Expr arg) {
  Expr e;
  e.value = std::make_pair(
    op, std::make_unique<Expr>(std::move(arg)));
  return e;
}

Expr makeBinary(Expr left, char op, Expr right) {
  Expr e;
  e.value = std::make_tuple(
    std::make_unique<Expr>(std::move(left)), 
    op,
    std::make_unique<Expr>(std::move(right))
  );
  return e;
}

// Example usage
Expr exampleExpr = makeBinary(
  makeUnary('-', makeVariable("x")),
  '+',
  makeIntegral(42)
);

This creates an Expr that represents the expression -x + 42.

The key points are:

  1. Use a smart pointer like std::unique_ptr to allow for recursive containment without requiring a fixed size.
  2. Wrap the variant in a struct or class to allow for a named recursive type.
  3. Use constructor functions (like makeIntegral, makeUnary etc in the example) to simplify the creation of complex recursive variants.

Recursive variants are a powerful tool for representing tree-like data structures in a type-safe way. They are often used in parsers, abstract syntax trees, and similar hierarchical data structures.

Answers to questions are automatically generated and may not have been reviewed.

A computer programmer
Part of the course:

Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects.

Free, unlimited access

This course includes:

  • 124 Lessons
  • 550+ Code Samples
  • 96% Positive Reviews
  • Regularly Updated
  • Help and FAQ
Free, Unlimited Access

Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects.

Screenshot from Warhammer: Total War
Screenshot from Tomb Raider
Screenshot from Jedi: Fallen Order
Contact|Privacy Policy|Terms of Use
Copyright © 2024 - All Rights Reserved