markt1964
markt1964

Reputation: 2826

std variant and forward declarations

It's my understanding that std::variant cannot directly hold references.

However, std::reference_wrapper is a fully qualified type that can be put into things like std::vector, and since one can make a vector of reference wrappers, I imagined that one could do the same with std::variant.

The following (edited to be more minimal) code generates a plethora of errors in gcc:

#include <functional>
#include <variant>

class Foo;
class Baz;

template<typename T> struct CRef : std::reference_wrapper<const T> {
    CRef(const T &x) : std::reference_wrapper<const T>(x)
    {
    }
};    

template<typename... Args> struct Contains : public std::variant<CRef<Args>... > {
};

struct Foo : public Contains<Baz> {

    int getSize() const;
};

struct getSizeVisitor {

    template<typename T> int operator()(CRef<T> x) const
    {
        return sizeof(T);
    }
};

inline int Foo::getSize() const
{
    return std::visit(getSizeVisitor(), (*this));
}

struct Baz : public Foo {

};

The CRef template is simply a convenient wrapper around an std::reference_wrapper to a const reference, and the Contains template exists to help a class be made aware of all of the endpoint subclasses that the base class may at some point refer to. In the above case, I am simply wanting a getSize() method, which will return the size of the actual type contained in the variant. Baz, in this case, is the only endpoint class, although in practice there will be more, and they will have no common base class, which is why I require a variant, and cannot simply use a base class and employ virtual functions.

The errors generated by the compiler are visible here: https://godbolt.org/z/lcbPjB

So, I guess I'm probably doing something that isn't allowed.

The question I have is, is there a way to do what I'm trying? I apologize in advance if my intent is unclear. If there are problems understanding what I am trying to achieve, some feedback to that effect that specifically identifies what additional information I need to supply can be left and I shall endeavor to comply.

Bear in mind that in the actual use case, the endpoint classes are much more complex, and there will be many more functions beyond getSize(), but I expect once I have something working for this simple case, I should be able to generalize and implement the other functions correctly.

Upvotes: 1

Views: 940

Answers (1)

Barry
Barry

Reputation: 303087

Here's a much shorter reproduction of the same issue:

int getSize(std::variant<int, char> var)
{
    return std::visit([](auto const& x){ return sizeof(x); },
        std::cref(var));
}

The problem is, you're trying to pass a reference_wrapper to std::visit... but that violates the requirements of std::visit - it needs take actual std::variants (in the original OP, the object is even more removed from std::variant - it's a reference wrapper of a type that inherits from a type that inherits from a std::variant - but the distance from variant doesn't matter).

You need to pass in the exact variant. In my short example, that's just passing var instead of std::cref(var). In the OP, that's casting *this down to variant<Ts...> const& for the correct types Ts...

Upvotes: 1

Related Questions