3

I'm deriving from a base class in which I try to define a type. That type is dependent on itself via a variant, so it would require to know the memory layout of the base class upon definition. However, I'm not prematurely defining the type? So I thought this would work but it doesnt (CompilerExplorer):

#include <variant>
#include <cstddef>
#include <array>


template <size_t V> struct container_base;
template <size_t V> struct container;

template <size_t V>
using val = std::variant<std::monostate, int, container_base<V>>;

template <size_t V>
struct container_base
{
    using iterator = typename std::array<val<V>, V>::iterator;
};

template <size_t V>
struct container : public container_base<V>
{
};

int main()
{
    container<100> A;
}

This yields:

In file included from /opt/compiler-explorer/gcc-trunk-20220726/include/c++/13.0.0/variant:37,
                 from <source>:1:
/opt/compiler-explorer/gcc-trunk-20220726/include/c++/13.0.0/type_traits: In instantiation of 'struct std::is_copy_constructible<container_base<100> >':
/opt/compiler-explorer/gcc-trunk-20220726/include/c++/13.0.0/type_traits:3202:33:   required from 'constexpr const bool std::is_copy_constructible_v<container_base<100> >'
/opt/compiler-explorer/gcc-trunk-20220726/include/c++/13.0.0/variant:329:5:   required from 'constexpr const bool std::__detail::__variant::_Traits<std::monostate, int, container_base<100> >::_S_copy_ctor'
/opt/compiler-explorer/gcc-trunk-20220726/include/c++/13.0.0/variant:1334:11:   required from 'class std::variant<std::monostate, int, container_base<100> >'
/opt/compiler-explorer/gcc-trunk-20220726/include/c++/13.0.0/array:109:55:   required from 'struct std::array<std::variant<std::monostate, int, container_base<100> >, 100>'
<source>:15:11:   required from 'struct container_base<100>'
<source>:19:8:   required from 'struct container<100>'
<source>:25:20:   required from here
/opt/compiler-explorer/gcc-trunk-20220726/include/c++/13.0.0/type_traits:1012:52: error: static assertion failed: template argument must be a complete class or an unbounded array
 1012 |       static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}),
      |

I understand it so that variant needs a complete type to work with when it's first used (which is in main! But the type is complete until then, so I don't know why it doesn't work. What can I do to fix this?

glades
  • 3,778
  • 1
  • 12
  • 34
  • I@AlanBirtles I know about members but is this also true for types? They don't contribute to the memory layout after all – glades Jul 28 '22 at 06:26
  • 1
    `typename std::array, V>::iterator` requires `val` to be complete. But this can't happen because, at that point, `container_base` (which `val` depends upon) has not been defined. If you define `container_base::iterator = val*`, your code compiles. – paolo Jul 28 '22 at 06:48
  • @paolo Thanks makes sense. What are the C++ rules for qualified access (::)? Does the qualifier always have to be defineable at this point? – glades Jul 28 '22 at 07:20
  • "**[res.on.functions]/2** In particular, the effects are undefined in the following cases: ... (2.5) — If an incomplete type is used as a template argument when instantiating a template component or evaluating a concept, unless specifically allowed for that component." Accessing `std::array, V>::iterator` requires instantiating `std::array, V>`, which requires instantiating `val`, which requires instantiating `std::variant>`, and `container_base` is at that point an incomplete type. – Igor Tandetnik Jul 28 '22 at 16:04

0 Answers0