Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

incomplete types with std::map and std::variant

Consider this simplified and very specific implementation of a recursive variant on top of std::variant:

#include <map>
#include <variant>

struct recursive_tag;

template <typename...>
struct RecursiveVariant;

template <>
struct RecursiveVariant<int, std::map<int, recursive_tag>>
    : std::variant<int, std::map<int, RecursiveVariant<int, std::map<int, recursive_tag>>>>
{
    using underlying = std::variant<int,
          std::map<int, RecursiveVariant<int, std::map<int, recursive_tag>>>>;
    using underlying::underlying;
};


int main() {
    RecursiveVariant<int, std::map<int, recursive_tag>> rv; 
}

This fails to compile on gcc 7/8 due to trying to instantiate std::pair<const int, recursive_tag>, which itself fails because recursive_tag is an incomplete type.

But, nothing in the compiler error call-stack indicates to me why std::pair<const int, recursive_tag> needs to be instantiated. The top line there is:

variant:252:48: required from ‘void std::__detail::__variant::__erased_dtor(_Variant&&) [with _Variant = const std::__detail::__variant::_Variant_storage<false, int, std::map<int, RecursiveVariant<int, std::map<int, recursive_tag, std::less<int>, std::allocator<std::pair<const int, recursive_tag> > > >, std::less<int>, std::allocator<std::pair<const int, RecursiveVariant<int, std::map<int, recursive_tag, std::less<int>, std::allocator<std::pair<const int, recursive_tag> > > > > > > >&; long unsigned int _Np = 0]

pointing to:

249   template<typename _Variant, size_t _Np>
250     void
251     __erased_dtor(_Variant&& __v)
252     { std::_Destroy(std::__addressof(__get<_Np>(__v))); }

While type map<int, recursive_tag> is spelled in there, the actual map type that should be instantiated is map<int, RecursiveVariant<int, map<int, recursive_tag>>>... which should only necessitate the instantiation of pair<const int, RecursiveVariant<...>>.

Simply making recursive_tag complete (i.e. by adding {}) fixes the problem. But what causes the problem to begin with?

like image 489
Barry Avatar asked May 29 '18 16:05

Barry


1 Answers

The line at issue calls

std::_Destroy(std::__addressof(__get<_Np>(__v)));

The need to perform ADL for __get is sufficient to trigger instantiation of any and all associated classes of the type of __v, i.e., _Variant, to look for potential friend functions (and function templates) with that name defined within these classes. That includes the pair that tripped you up.

like image 103
T.C. Avatar answered Oct 21 '22 14:10

T.C.