Consider the following code snippet:
struct v : std::variant<int, std::vector<v>> { };
int main()
{
std::visit([](auto){ }, v{0});
}
clang++ 7 with -stdlib=libc++ -std=c++2a
compiles the code;
g++ 9 with -std=c++2a
fails to compile the code, with the following error:
/opt/compiler-explorer/gcc-trunk-20180711/include/c++/9.0.0/variant:94:29: error: incomplete type 'std::variant_size' used in nested name specifier
inline constexpr size_t variant_size_v = variant_size<_Variant>::value; ^~~~~~~~~~~~~~
live example on godbolt.org
Are both implementations conforming to the Standard?
If not, what implementation is correct here, and why?
[variant.visit] in C++17 doesn't use variant_size_v
, but it does in the current working draft as a result of an editorial change. I don't see any indication that LWG reviewed the change before it went in, but it has looked at this part of the standard several times since then and has yet to object to it, so I'm going to postulate that it is in fact required.
Meanwhile, LWG issue 3052, which has been referred to LEWG, would explicitly require std::variant
. When that issue is resolved - one way or the other - it should resolve this too.
Looks like it is a bug in gcc implementation. According to cppreference, it is called as if calling invoke
on a std::get
. std::get<>
is defined for anything which is convertible to std::variant
(since it accepts a std::variant
argument by forwarding reference). Your structure is convertible to std::variant
, and so std::get
itself works on your structure in gcc.
The fact that the gcc implementation chose to use a std::variant_size
as part of its implementation of visit
is their implementation detail, and the fact that it doesn't (and shouldn't) work for your struct is irrelevant.
Conclusion: It is a bug in gcc due to an oversight in implementation.
I came across this issue as well recently. I kind of came up with a workaround which basically specialises variant_size and variant_alternative for the class that inherits from the variant..
link on godbolt
Its not pretty and it injects stuff into the std namespace. I'm not a metaprogramming expert (yet!) so its something I hacked together. Maybe someone else can improve on this?
#include <variant>
#include <string>
#include <vector>
#include <iostream>
#include <utility>
#include <type_traits>
using var = std::variant<int, bool, float, std::string>;
struct myvar : public var {
using var::var;
using var::operator=;
};
namespace std{
template<>
struct variant_size<myvar> : variant_size<var> {
};
template<std::size_t I>
struct variant_alternative<I,myvar> : variant_alternative<I,var> {
};
}
int main(){
constexpr int vs = std::variant_size<var>::value;
myvar s = std::string{"boo!"};
std::visit([](auto&& e){std::cout << e << "\n";}, s);
std::cout << vs;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With