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?