Using std::visit on a class inheriting from std::variant - libstdc++ vs libc++

ⅰ亾dé卋堺 提交于 2019-12-04 16:50:21

问题


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?


回答1:


[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.




回答2:


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.




回答3:


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;
}



来源:https://stackoverflow.com/questions/51309467/using-stdvisit-on-a-class-inheriting-from-stdvariant-libstdc-vs-libc

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!