1

Consider the following:

#include <variant>
#include <iostream>

int main ( )
{
    std::variant<int, float> v { 10.f };

    std::cout << v.index() << std::endl;
}

This works perfectly, and outputs 1.

Now, suppose we want to output the variant into std::cout using a pretty generic output operator that doesn't even do anything:

#include <variant>
#include <iostream>

template <typename ... Args>
std::ostream & operator << (std::ostream & os, std::variant<Args...> const & v)
{
    return os;
}

int main ( )
{
    std::variant<int, float> v { 10.f };

    std::cout <<  v.index() << std::endl;
}

All compilers that I've tested (gcc-7.1.0, gcc-7.2.0, gcc-7.3.0, gcc-8.1.0, gcc-8.2.0, gcc-8.3.0, gcc-9.1.0, clang-7, clang-8) produce similar errors, namely

/usr/lib/gcc/x86_64-pc-linux-gnu/9.1.0/include/g++-v9/variant: In instantiation of ‘constexpr const bool std::__detail::__variant::_Traits<>::_S_default_ctor’:
/usr/lib/gcc/x86_64-pc-linux-gnu/9.1.0/include/g++-v9/variant:1219:11:   required from ‘class std::variant<>’
a.cpp:14:34:   required from here
/usr/lib/gcc/x86_64-pc-linux-gnu/9.1.0/include/g++-v9/variant:273:4: error: invalid use of incomplete type ‘struct std::__detail::__variant::_Nth_type<0>’
  273 |    is_default_constructible_v<typename _Nth_type<0, _Types...>::type>;
  |    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/lib/gcc/x86_64-pc-linux-gnu/9.1.0/include/g++-v9/variant:58:12: note: declaration of ‘struct std::__detail::__variant::_Nth_type<0>’
   58 |     struct _Nth_type;
  |            ^~~~~~~~~
/usr/lib/gcc/x86_64-pc-linux-gnu/9.1.0/include/g++-v9/variant: In instantiation of ‘class std::variant<>’:
a.cpp:14:34:   required from here
/usr/lib/gcc/x86_64-pc-linux-gnu/9.1.0/include/g++-v9/variant:1238:39: error: static assertion failed: variant must have at least one alternative
 1238 |       static_assert(sizeof...(_Types) > 0,
      |                     ~~~~~~~~~~~~~~~~~~^~~
In file included from /usr/lib/gcc/x86_64-pc-linux-gnu/9.1.0/include/g++-v9/bits/move.h:55,
             from /usr/lib/gcc/x86_64-pc-linux-gnu/9.1.0/include/g++-v9/bits/nested_exception.h:40,
             from /usr/lib/gcc/x86_64-pc-linux-gnu/9.1.0/include/g++-v9/exception:144,
             from /usr/lib/gcc/x86_64-pc-linux-gnu/9.1.0/include/g++-v9/ios:39,
             from /usr/lib/gcc/x86_64-pc-linux-gnu/9.1.0/include/g++-v9/ostream:38,
             from /usr/lib/gcc/x86_64-pc-linux-gnu/9.1.0/include/g++-v9/iostream:39,
             from a.cpp:1:
/usr/lib/gcc/x86_64-pc-linux-gnu/9.1.0/include/g++-v9/type_traits: In substitution of ‘template<bool _Cond, class _Tp> using enable_if_t = typename std::enable_if::type [with bool _Cond = (0 != 0); _Tp = void]’:
/usr/lib/gcc/x86_64-pc-linux-gnu/9.1.0/include/g++-v9/variant:1301:2:   required from ‘class std::variant<>’
a.cpp:14:34:   required from here
/usr/lib/gcc/x86_64-pc-linux-gnu/9.1.0/include/g++-v9/type_traits:2426:11: error: no type named ‘type’ in ‘struct std::enable_if<false, void>’
 2426 |     using enable_if_t = typename enable_if<_Cond, _Tp>::type;
      |           ^~~~~~~~~~~

Basically, it complains about instantiating an empty variant (with empty type list) at the std::cout << ... line. Note that we don't even call the operator << that we added. Renaming this operator, or making the Args... explicitly nonempty (changing to variant<T, Args...>) makes the error disappear.

What is happening and why is it happening? Am I missing something, or is it (unlikely) a bug of all the aforementioned compilers?

lisyarus
  • 15,025
  • 3
  • 43
  • 68

0 Answers0