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