16

This code:

class Foo {
    std::unordered_map<std::string, Foo> x;
};

gives me an error:

/usr/include/c++/4.7/bits/stl_pair.h:94:11:
  error: 'std::pair<_T1, _T2>::second' has incomplete type
foo.cpp:4:7: error: forward declaration of 'class Foo'

However, this code compiles just fine:

class Foo {
    std::vector<Foo> x;
};

Is this a library/compiler bug?

Mat
  • 202,337
  • 40
  • 393
  • 406
Martin C. Martin
  • 3,565
  • 3
  • 29
  • 36
  • 1
    @Xeo: It is explicitly not a bug, because (I quote) "the Standard places no requirements on the implementation." If a bug is failure to adhere to a requirement, and there are no requirements, then it cannot be a bug. – Ben Voigt Oct 26 '12 at 15:22
  • You can always use Boost.Container. They explicitly allow instatiation with incomplete types. See http://www.boost.org/doc/libs/1_51_0/doc/html/container/containers_of_incomplete_types.html and http://www.drdobbs.com/the-standard-librarian-containers-of-inc/184403814 – pmr Oct 26 '12 at 15:29
  • GNU `libstdc++` was the last where this error happened. But now one can see that GCC trunk accepts the code, joining Clang and MSVC: https://gcc.godbolt.org/z/Y6oeGcbq8 – Fedor Jan 30 '22 at 10:19

1 Answers1

22

The C++ standard specifies for the various smart pointers that the template parameter is allowed to be an incomplete type.

The 2017 and later versions of the standard allow a container's value_type to be an incomplete type when instantiating the class template only for the container templates std::forward_list, std::list, and std::vector, and only if the allocator type satisfies the "allocator completeness requirements". The default allocator template std::allocator always satisfies the allocator completeness requirements. Instantiating any member of the container class template still requires the value_type to be complete.

For any other standard container type, this information is not given. In cases where it is unspecified, an implementation is allowed to accept an incomplete type for one container class template and not another, and still be conformant.

To make your code portable, avoid making containers of any type before the type is completed, except in the cases specifically permitted by the standard.

Formally, the general constraint is found in the following rule ([res.on.functions]) which applies to your code:

In certain cases (replacement functions, handler functions, operations on types used to instantiate standard library template components), the C++ standard library depends on components supplied by a C++ program. If these components do not meet their requirements, the Standard places no requirements on the implementation.

In particular, the effects are undefined in the following cases:

...

  • if an incomplete type is used as a template argument when instantiating a template component, unless specifically allowed for that component.

The three statements specifically allowing an incomplete template argument for forward_list, list, and vector are found in sections [forwardlist.overview], [list.overview], and [vector.overview].

aschepler
  • 70,891
  • 9
  • 107
  • 161
Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • So "the effects are undefined" does not mean "undefined behavior"? – sbi Oct 28 '12 at 20:27
  • @sbi: It means "undefined behavior" if and only if the implementation allows it to compile and doesn't provide a stronger guarantee. But typically you'll get translation failure, not undefined runtime behavior. – Ben Voigt Oct 29 '12 at 15:19