2

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.

  • What compiler are you using? [this compiles](https://godbolt.org/z/h3bnG965c) – KamilCuk Dec 29 '21 at 13:51
  • @KamilCuk - I want to avoid undefined behavior. Unfortunately successful compilation on some compiler is no guarantee of that. –  Dec 29 '21 at 13:54

1 Answers1

3

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.

user17732522
  • 53,019
  • 2
  • 56
  • 105
  • Would `std::map` be a suitable extra level of indirection? – Eljay Dec 29 '21 at 14:43
  • @Eljay Sure, but you don't need to make it that complicated. `std::map>` in OP's example would be enough. `std::unique_ptr` does not require it's type to be complete. – user17732522 Dec 29 '21 at 14:45