#include <variant>
#include <vector>
struct A;
using B = std::variant<A>;
struct A {
std::vector<B> v;
};
int main() {
A a;
}
It compiles with GCC, clang and MSVC.
When I replace std::vector
with std::optional
it no longer works.
#include <variant>
#include <optional>
struct A;
using B = std::variant<A>;
struct A {
std::optional<B> v;
};
int main() {
A a;
}
clang + libstdc++:
In file included from <source>:1:
In file included from /opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/variant:40:
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/type_traits:3416:7: error: incomplete type 'A' used in type trait expression
3416 | = __is_trivially_constructible(_Tp, __add_rval_ref_t<_Tp>);
| ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/variant:372:5: note: in instantiation of variable template specialization 'std::is_trivially_move_constructible_v<A>' requested here
372 | (is_trivially_move_constructible_v<_Types> && ...);
| ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/variant:377:23: note: in instantiation of static data member 'std::__detail::__variant::_Traits<A>::_S_trivial_move_ctor' requested here
377 | _S_trivial_dtor && _S_trivial_move_ctor
| ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/variant:759:45: note: in instantiation of static data member 'std::__detail::__variant::_Traits<A>::_S_trivial_move_assign' requested here
759 | _Move_assign_base<_Traits<_Types...>::_S_trivial_move_assign, _Types...>;
| ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/variant:762:28: note: in instantiation of template type alias '_Move_assign_alias' requested here
762 | struct _Variant_base : _Move_assign_alias<_Types...>
| ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/variant:1362:15: note: in instantiation of template class 'std::__detail::__variant::_Variant_base<A>' requested here
1362 | : private __detail::__variant::_Variant_base<_Types...>,
| ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/type_traits:3413:7: note: in instantiation of template class 'std::variant<A>' requested here
3413 | = __is_trivially_constructible(_Tp, __add_lval_ref_t<const _Tp>);
| ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/optional:506:12: note: in instantiation of variable template specialization 'std::is_trivially_copy_constructible_v<std::variant<A>>' requested here
506 | bool = is_trivially_copy_constructible_v<_Tp>,
| ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/optional:704:15: note: in instantiation of default argument for '_Optional_base<std::variant<A>>' required here
704 | : private _Optional_base<_Tp>,
| ^~~~~~~~~~~~~~~~~~~
<source>:9:22: note: in instantiation of template class 'std::optional<std::variant<A>>' requested here
9 | std::optional<B> v;
| ^
<source>:8:8: note: definition of 'A' is not complete until the closing '}'
8 | struct A {
| ^
In file included from <source>:1:
In file included from /opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/variant:40:
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/type_traits:3413:7: error: incomplete type 'A' used in type trait expression
3413 | = __is_trivially_constructible(_Tp, __add_lval_ref_t<const _Tp>);
| ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/variant:370:5: note: in instantiation of variable template specialization 'std::is_trivially_copy_constructible_v<A>' requested here
370 | (is_trivially_copy_constructible_v<_Types> && ...);
| ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/variant:374:23: note: in instantiation of static data member 'std::__detail::__variant::_Traits<A>::_S_trivial_copy_ctor' requested here
374 | _S_trivial_dtor && _S_trivial_copy_ctor
| ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/variant:705:45: note: in instantiation of static data member 'std::__detail::__variant::_Traits<A>::_S_trivial_copy_assign' requested here
705 | _Copy_assign_base<_Traits<_Types...>::_S_trivial_copy_assign, _Types...>;
| ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/variant:708:32: note: in instantiation of template type alias '_Copy_assign_alias' requested here
708 | struct _Move_assign_base : _Copy_assign_alias<_Types...>
| ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/variant:762:28: note: in instantiation of template class 'std::__detail::__variant::_Move_assign_base<false, A>' requested here
762 | struct _Variant_base : _Move_assign_alias<_Types...>
| ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/variant:1362:15: note: in instantiation of template class 'std::__detail::__variant::_Variant_base<A>' requested here
1362 | : private __detail::__variant::_Variant_base<_Types...>,
| ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/type_traits:3413:7: note: in instantiation of template class 'std::variant<A>' requested here
3413 | = __is_trivially_constructible(_Tp, __add_lval_ref_t<const _Tp>);
| ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/optional:506:12: note: in instantiation of variable template specialization 'std::is_trivially_copy_constructible_v<std::variant<A>>' requested here
506 | bool = is_trivially_copy_constructible_v<_Tp>,
| ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/optional:704:15: note: in instantiation of default argument for '_Optional_base<std::variant<A>>' required here
704 | : private _Optional_base<_Tp>,
| ^~~~~~~~~~~~~~~~~~~
<source>:9:22: note: in instantiation of template class 'std::optional<std::variant<A>>' requested here
9 | std::optional<B> v;
| ^
<source>:8:8: note: definition of 'A' is not complete until the closing '}'
8 | struct A {
| ^
In file included from <source>:1:
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/variant:276:8: error: field has incomplete type 'A'
276 | _Type _M_storage;
| ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/variant:274:7: note: in instantiation of member class 'std::__detail::__variant::_Uninitialized<A>::(anonymous union at /opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/variant:274:7)' requested here
274 | union {
| ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/variant:434:30: note: in instantiation of template class 'std::__detail::__variant::_Uninitialized<A>' requested here
434 | _Uninitialized<_First> _M_first;
| ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/variant:516:41: note: in instantiation of template class 'std::__detail::__variant::_Variadic_union<false, A>' requested here
516 | _Variadic_union<false, _Types...> _M_u;
| ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/variant:581:30: note: in instantiation of template class 'std::__detail::__variant::_Variant_storage<false, A>' requested here
581 | struct _Copy_ctor_base : _Variant_storage_alias<_Types...>
| ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/variant:618:30: note: in instantiation of template class 'std::__detail::__variant::_Copy_ctor_base<false, A>' requested here
618 | struct _Move_ctor_base : _Copy_ctor_alias<_Types...>
| ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/variant:656:32: note: (skipping 3 contexts in backtrace; use -ftemplate-backtrace-limit=0 to see all)
656 | struct _Copy_assign_base : _Move_ctor_alias<_Types...>
| ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/variant:1362:15: note: in instantiation of template class 'std::__detail::__variant::_Variant_base<A>' requested here
1362 | : private __detail::__variant::_Variant_base<_Types...>,
| ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/type_traits:3413:7: note: in instantiation of template class 'std::variant<A>' requested here
3413 | = __is_trivially_constructible(_Tp, __add_lval_ref_t<const _Tp>);
| ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/optional:506:12: note: in instantiation of variable template specialization 'std::is_trivially_copy_constructible_v<std::variant<A>>' requested here
506 | bool = is_trivially_copy_constructible_v<_Tp>,
| ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/optional:704:15: note: in instantiation of default argument for '_Optional_base<std::variant<A>>' required here
704 | : private _Optional_base<_Tp>,
| ^~~~~~~~~~~~~~~~~~~
<source>:9:22: note: in instantiation of template class 'std::optional<std::variant<A>>' requested here
9 | std::optional<B> v;
| ^
<source>:8:8: note: definition of 'A' is not complete until the closing '}'
8 | struct A {
| ^
In file included from <source>:1:
In file included from /opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/variant:40:
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/type_traits:3388:7: error: incomplete type 'A' used in type trait expression
3388 | = __is_constructible(_Tp, __add_lval_ref_t<const _Tp>);
| ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/variant:357:5: note: in instantiation of variable template specialization 'std::is_copy_constructible_v<A>' requested here
357 | (is_copy_constructible_v<_Types> && ...);
| ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/variant:1364:43: note: in instantiation of static data member 'std::__detail::__variant::_Traits<A>::_S_copy_ctor' requested here
1364 | __detail::__variant::_Traits<_Types...>::_S_copy_ctor,
| ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/type_traits:3413:7: note: in instantiation of template class 'std::variant<A>' requested here
3413 | = __is_trivially_constructible(_Tp, __add_lval_ref_t<const _Tp>);
| ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/optional:506:12: note: in instantiation of variable template specialization 'std::is_trivially_copy_constructible_v<std::variant<A>>' requested here
506 | bool = is_trivially_copy_constructible_v<_Tp>,
| ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/optional:704:15: note: in instantiation of default argument for '_Optional_base<std::variant<A>>' required here
704 | : private _Optional_base<_Tp>,
| ^~~~~~~~~~~~~~~~~~~
<source>:9:22: note: in instantiation of template class 'std::optional<std::variant<A>>' requested here
9 | std::optional<B> v;
| ^
<source>:8:8: note: definition of 'A' is not complete until the closing '}'
8 | struct A {
| ^
In file included from <source>:1:
In file included from /opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/variant:40:
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/type_traits:3391:7: error: incomplete type 'A' used in type trait expression
3391 | = __is_constructible(_Tp, __add_rval_ref_t<_Tp>);
| ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/variant:359:5: note: in instantiation of variable template specialization 'std::is_move_constructible_v<A>' requested here
359 | (is_move_constructible_v<_Types> && ...);
| ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/variant:1366:43: note: in instantiation of static data member 'std::__detail::__variant::_Traits<A>::_S_move_ctor' requested here
1366 | __detail::__variant::_Traits<_Types...>::_S_move_ctor,
| ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/type_traits:3413:7: note: in instantiation of template class 'std::variant<A>' requested here
3413 | = __is_trivially_constructible(_Tp, __add_lval_ref_t<const _Tp>);
| ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/optional:506:12: note: in instantiation of variable template specialization 'std::is_trivially_copy_constructible_v<std::variant<A>>' requested here
506 | bool = is_trivially_copy_constructible_v<_Tp>,
| ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/optional:704:15: note: in instantiation of default argument for '_Optional_base<std::variant<A>>' required here
704 | : private _Optional_base<_Tp>,
| ^~~~~~~~~~~~~~~~~~~
<source>:9:22: note: in instantiation of template class 'std::optional<std::variant<A>>' requested here
9 | std::optional<B> v;
| ^
<source>:8:8: note: definition of 'A' is not complete until the closing '}'
8 | struct A {
| ^
In file included from <source>:1:
In file included from /opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/variant:40:
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/type_traits:3385:54: error: incomplete type 'A' used in type trait expression
3385 | inline constexpr bool is_default_constructible_v = __is_constructible(_Tp);
| ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/variant:1422:26: note: in instantiation of variable template specialization 'std::is_default_constructible_v<A>' requested here
1422 | variant() requires is_default_constructible_v<__to_type<0>> = default;
| ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/variant:1422:26: note: while substituting template arguments into constraint expression here
1422 | variant() requires is_default_constructible_v<__to_type<0>> = default;
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/variant:1422:7: note: while checking constraint satisfaction for function 'variant' required here
1422 | variant() requires is_default_constructible_v<__to_type<0>> = default;
| ^~~~~~~
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/type_traits:3413:7: note: in instantiation of template class 'std::variant<A>' requested here
3413 | = __is_trivially_constructible(_Tp, __add_lval_ref_t<const _Tp>);
| ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/optional:506:12: note: in instantiation of variable template specialization 'std::is_trivially_copy_constructible_v<std::variant<A>>' requested here
506 | bool = is_trivially_copy_constructible_v<_Tp>,
| ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/optional:704:15: note: in instantiation of default argument for '_Optional_base<std::variant<A>>' required here
704 | : private _Optional_base<_Tp>,
| ^~~~~~~~~~~~~~~~~~~
<source>:9:22: note: in instantiation of template class 'std::optional<std::variant<A>>' requested here
9 | std::optional<B> v;
| ^
<source>:8:8: note: definition of 'A' is not complete until the closing '}'
8 | struct A {
| ^
I suppose it works because all std::vector
implementations have their data on the heap. But that's not required by the standard, is it? If I'm not mistaken, it may be possible that some std::vector
implementations do some kind of small data optimisation, making it similar to std::optional
.
std::vector
supportes incomplete types by C++17 26.3.11.1/3:
An incomplete type T may be used when instantiating vector if the allocator satisfies the allocator completeness requirements (20.5.3.5.1). T shall be complete before any member of the resulting specialization of vector is referenced.
The code is therefore always defined behaviour.
Many thanks to Jarod42 for mentioning this in the comments and putting me on the right track! Please correct me if my conclusion is wrong. I don't have much experience in how to answer language-lawyer questions. ;-)
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