Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is A::member of vector of variant of A undefined behaviour? [duplicate]

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

like image 657
Benjamin Buch Avatar asked Oct 20 '25 02:10

Benjamin Buch


1 Answers

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. ;-)

like image 125
Benjamin Buch Avatar answered Oct 21 '25 17:10

Benjamin Buch



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!