user17789309
user17789309

Reputation:

C++ variant containing a map of itself

I would like to be able to create a variant that contains a std::map<std::string, MyVariant> as one of its cases. The ideal would be able to write something like

using MyVariant = std::variant<int, std::string, std::map<std::string, MyVariant>>;

but this requires forward declaration.

I'm aware that similar questions have been asked previously, e.g. here and here, but these have mainly been focused on the case of std::vector, and since C++17 std::vector is allowed to use incomplete types, while std::map does not.

In particular, I'm wondering if the fixed-point combinator solution in this answer would work in this case? Adapting the code from that answer:

#include <map>
#include <string>
#include <variant>

// non-recursive definition
template<typename T>
using VariantImpl = std::variant<int, std::string, std::map<std::string, T>>;

// fixed-point combinator
template<template<typename> typename K>
struct FixCombinator : K<FixCombinator<K>>
{
    using K<FixCombinator>::K;
};

using MyVariant = FixCombinator<VariantImpl>;

However if there's another way to do it, I would be interested with that also.

Upvotes: 2

Views: 1009

Answers (1)

user17732522
user17732522

Reputation: 76658

This is not possible (at least by standard guarantees), since std::variant requires the used types to be complete and std::map requires key and value type to be complete types at the point of instantiation. But your construction will be complete only after it's instantiation.

The only standard containers allowing such a recursive construction (at least to some degree) are std::vector, std::list and std::forward_list.

If you want to use std::map and have standard guarantees for it, you need to add one level of indirection at some point.

Upvotes: 4

Related Questions