Reputation: 2826
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
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::variant
s (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