A compilation error just including parts of the standard library and this library in a specific order when using C++17 and GCC 10.
Consider the following file:
#include <any>
#include <nlohmann/json>
When compiling it in C++17 mode, it won't compile with GCC 10
Here's a compile explorer link that shows the issue.
When changing <any> to <variant>, there is no issue. I don't know if any other standard header causes this bug.
When putting the #include <any> after #include <nlohmann/json>, it compiles. DEMO.
It should compile.
It shows very strange errors and fail to compile.
develop branchI had issues with some CMake related tests but it seem unrelated to this bug.
For reference, here is the error message from godbolt:
In file included from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/bits/move.h:57,
from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/bits/nested_exception.h:40,
from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/exception:148,
from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/new:41,
from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/any:37,
from <source>:1:
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits: In instantiation of 'struct std::is_constructible<nlohmann::basic_json<>, const nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer>&>':
#### json.nlohmann/json.hpp:11120:79: required by substitution of 'template<class ... Args, typename std::enable_if<std::is_constructible<nlohmann::basic_json<>, Args ...>::value, int>::type <anonymous> > nlohmann::detail::json_ref<nlohmann::basic_json<> >::json_ref(Args&& ...) [with Args = {const nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer>&}; typename std::enable_if<std::is_constructible<nlohmann::basic_json<>, Args ...>::value, int>::type <anonymous> = <missing>]'
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits:901:30: required from 'struct std::__is_constructible_impl<nlohmann::basic_json<>, const nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer>&>'
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits:930:12: required from 'struct std::__is_copy_constructible_impl<nlohmann::basic_json<>, true>'
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits:936:12: required from 'struct std::is_copy_constructible<nlohmann::basic_json<> >'
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/any:185:49: required by substitution of 'template<class _Tp, class _VTp, class _Mgr, typename std::enable_if<(std::is_copy_constructible<_Tp>::value && (! std::__is_in_place_type<_VTp>::value)), bool>::type <anonymous> > std::any::any(_Tp&&) [with _Tp = nlohmann::basic_json<>&; _VTp = nlohmann::basic_json<>; _Mgr = std::any::_Manager_external<nlohmann::basic_json<> >; typename std::enable_if<(std::is_copy_constructible<_Tp>::value && (! std::__is_in_place_type<_VTp>::value)), bool>::type <anonymous> = <missing>]'
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits:2670:31: required by substitution of 'template<class _Tp, class> static std::true_type std::__swappable_details::__do_is_swappable_impl::__test(int) [with _Tp = nlohmann::basic_json<>; <template-parameter-1-2> = <missing>]'
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits:2694:35: required from 'struct std::__is_swappable_impl<nlohmann::basic_json<> >'
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits:2720:12: required from 'struct std::is_swappable<nlohmann::basic_json<> >'
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits:2740:26: required from 'constexpr const bool std::is_swappable_v<nlohmann::basic_json<> >'
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/optional:1201:51: required by substitution of 'template<class _Tp> std::enable_if_t<(!(is_move_constructible_v<_Tp> && is_swappable_v<_Tp>))> std::swap(std::optional<_Tp>&, std::optional<_Tp>&) [with _Tp = nlohmann::basic_json<>]'
#### json.nlohmann/json.hpp:22677:1: required from here
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits:906:12: error: invalid use of incomplete type 'struct std::__is_constructible_impl<nlohmann::basic_json<>, const nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer>&>'
906 | struct is_constructible
| ^~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits:900:12: note: declaration of 'struct std::__is_constructible_impl<nlohmann::basic_json<>, const nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer>&>'
900 | struct __is_constructible_impl
| ^~~~~~~~~~~~~~~~~~~~~~~
In file included from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/vector:66,
from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/functional:62,
from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/pstl/glue_algorithm_defs.h:13,
from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/algorithm:74,
from #### json.nlohmann/json.hpp:37:
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/bits/stl_uninitialized.h: In instantiation of '_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = __gnu_cxx::__normal_iterator<const nlohmann::basic_json<>*, std::vector<nlohmann::basic_json<>, std::allocator<nlohmann::basic_json<> > > >; _ForwardIterator = nlohmann::basic_json<>*]':
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/bits/stl_uninitialized.h:325:37: required from '_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator<_Tp>&) [with _InputIterator = __gnu_cxx::__normal_iterator<const nlohmann::basic_json<>*, std::vector<nlohmann::basic_json<>, std::allocator<nlohmann::basic_json<> > > >; _ForwardIterator = nlohmann::basic_json<>*; _Tp = nlohmann::basic_json<>]'
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/bits/stl_vector.h:558:31: required from 'std::vector<_Tp, _Alloc>::vector(const std::vector<_Tp, _Alloc>&) [with _Tp = nlohmann::basic_json<>; _Alloc = std::allocator<nlohmann::basic_json<> >]'
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/ext/new_allocator.h:150:4: required from 'void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = std::vector<nlohmann::basic_json<>, std::allocator<nlohmann::basic_json<> > >; _Args = {const std::vector<nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer>, std::allocator<nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer> > >&}; _Tp = std::vector<nlohmann::basic_json<>, std::allocator<nlohmann::basic_json<> > >]'
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/bits/alloc_traits.h:512:17: required from 'static void std::allocator_traits<std::allocator<_Tp1> >::construct(std::allocator_traits<std::allocator<_Tp1> >::allocator_type&, _Up*, _Args&& ...) [with _Up = std::vector<nlohmann::basic_json<>, std::allocator<nlohmann::basic_json<> > >; _Args = {const std::vector<nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer>, std::allocator<nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer> > >&}; _Tp = std::vector<nlohmann::basic_json<>, std::allocator<nlohmann::basic_json<> > >; std::allocator_traits<std::allocator<_Tp1> >::allocator_type = std::allocator<std::vector<nlohmann::basic_json<>, std::allocator<nlohmann::basic_json<> > > >]'
#### json.nlohmann/json.hpp:15381:35: [ skipping 2 instantiation contexts, use -ftemplate-backtrace-limit=0 to disable ]
#### json.nlohmann/json.hpp:16365:25: required from 'nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer>::basic_json(const nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer>&) [with ObjectType = std::map; ArrayType = std::vector; StringType = std::__cxx11::basic_string<char>; BooleanType = bool; NumberIntegerType = long int; NumberUnsignedType = long unsigned int; NumberFloatType = double; AllocatorType = std::allocator; JSONSerializer = nlohmann::adl_serializer]'
#### json.nlohmann/json.hpp:4737:31: required from 'bool nlohmann::detail::json_sax_dom_callback_parser<BasicJsonType>::end_object() [with BasicJsonType = nlohmann::basic_json<>]'
#### json.nlohmann/json.hpp:8847:33: required from 'bool nlohmann::detail::parser<BasicJsonType>::sax_parse_internal(SAX*) [with SAX = nlohmann::detail::json_sax_dom_callback_parser<nlohmann::basic_json<> >; BasicJsonType = nlohmann::basic_json<>]'
#### json.nlohmann/json.hpp:8738:31: required from 'void nlohmann::detail::parser<BasicJsonType>::parse(bool, BasicJsonType&) [with BasicJsonType = nlohmann::basic_json<>]'
#### json.nlohmann/json.hpp:20882:79: required from 'static nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer> nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer>::parse(IteratorType, IteratorType, nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer>::parser_callback_t, bool) [with IteratorType = const char*; typename std::enable_if<std::is_base_of<std::random_access_iterator_tag, typename std::iterator_traits<_InputIterator>::iterator_category>::value, int>::type <anonymous> = 0; ObjectType = std::map; ArrayType = std::vector; StringType = std::__cxx11::basic_string<char>; BooleanType = bool; NumberIntegerType = long int; NumberUnsignedType = long unsigned int; NumberFloatType = double; AllocatorType = std::allocator; JSONSerializer = nlohmann::adl_serializer; nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer>::parser_callback_t = std::function<bool(int, nlohmann::detail::parser<nlohmann::basic_json<> >::parse_event_t, nlohmann::basic_json<>&)>]'
#### json.nlohmann/json.hpp:22700:42: required from here
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/bits/stl_uninitialized.h:137:72: error: 'value' is not a member of 'std::is_constructible<nlohmann::basic_json<>, const nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer>&>'
137 | static_assert(is_constructible<_ValueType2, decltype(*__first)>::value,
| ^~~~~
ASM generation compiler returned: 1
In file included from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/bits/move.h:57,
from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/bits/nested_exception.h:40,
from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/exception:148,
from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/new:41,
from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/any:37,
from <source>:1:
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits: In instantiation of 'struct std::is_constructible<nlohmann::basic_json<>, const nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer>&>':
#### json.nlohmann/json.hpp:11120:79: required by substitution of 'template<class ... Args, typename std::enable_if<std::is_constructible<nlohmann::basic_json<>, Args ...>::value, int>::type <anonymous> > nlohmann::detail::json_ref<nlohmann::basic_json<> >::json_ref(Args&& ...) [with Args = {const nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer>&}; typename std::enable_if<std::is_constructible<nlohmann::basic_json<>, Args ...>::value, int>::type <anonymous> = <missing>]'
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits:901:30: required from 'struct std::__is_constructible_impl<nlohmann::basic_json<>, const nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer>&>'
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits:930:12: required from 'struct std::__is_copy_constructible_impl<nlohmann::basic_json<>, true>'
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits:936:12: required from 'struct std::is_copy_constructible<nlohmann::basic_json<> >'
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/any:185:49: required by substitution of 'template<class _Tp, class _VTp, class _Mgr, typename std::enable_if<(std::is_copy_constructible<_Tp>::value && (! std::__is_in_place_type<_VTp>::value)), bool>::type <anonymous> > std::any::any(_Tp&&) [with _Tp = nlohmann::basic_json<>&; _VTp = nlohmann::basic_json<>; _Mgr = std::any::_Manager_external<nlohmann::basic_json<> >; typename std::enable_if<(std::is_copy_constructible<_Tp>::value && (! std::__is_in_place_type<_VTp>::value)), bool>::type <anonymous> = <missing>]'
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits:2670:31: required by substitution of 'template<class _Tp, class> static std::true_type std::__swappable_details::__do_is_swappable_impl::__test(int) [with _Tp = nlohmann::basic_json<>; <template-parameter-1-2> = <missing>]'
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits:2694:35: required from 'struct std::__is_swappable_impl<nlohmann::basic_json<> >'
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits:2720:12: required from 'struct std::is_swappable<nlohmann::basic_json<> >'
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits:2740:26: required from 'constexpr const bool std::is_swappable_v<nlohmann::basic_json<> >'
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/optional:1201:51: required by substitution of 'template<class _Tp> std::enable_if_t<(!(is_move_constructible_v<_Tp> && is_swappable_v<_Tp>))> std::swap(std::optional<_Tp>&, std::optional<_Tp>&) [with _Tp = nlohmann::basic_json<>]'
#### json.nlohmann/json.hpp:22677:1: required from here
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits:906:12: error: invalid use of incomplete type 'struct std::__is_constructible_impl<nlohmann::basic_json<>, const nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer>&>'
906 | struct is_constructible
| ^~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits:900:12: note: declaration of 'struct std::__is_constructible_impl<nlohmann::basic_json<>, const nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer>&>'
900 | struct __is_constructible_impl
| ^~~~~~~~~~~~~~~~~~~~~~~
In file included from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/vector:66,
from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/functional:62,
from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/pstl/glue_algorithm_defs.h:13,
from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/algorithm:74,
from #### json.nlohmann/json.hpp:37:
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/bits/stl_uninitialized.h: In instantiation of '_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = __gnu_cxx::__normal_iterator<const nlohmann::basic_json<>*, std::vector<nlohmann::basic_json<>, std::allocator<nlohmann::basic_json<> > > >; _ForwardIterator = nlohmann::basic_json<>*]':
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/bits/stl_uninitialized.h:325:37: required from '_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator<_Tp>&) [with _InputIterator = __gnu_cxx::__normal_iterator<const nlohmann::basic_json<>*, std::vector<nlohmann::basic_json<>, std::allocator<nlohmann::basic_json<> > > >; _ForwardIterator = nlohmann::basic_json<>*; _Tp = nlohmann::basic_json<>]'
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/bits/stl_vector.h:558:31: required from 'std::vector<_Tp, _Alloc>::vector(const std::vector<_Tp, _Alloc>&) [with _Tp = nlohmann::basic_json<>; _Alloc = std::allocator<nlohmann::basic_json<> >]'
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/ext/new_allocator.h:150:4: required from 'void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = std::vector<nlohmann::basic_json<>, std::allocator<nlohmann::basic_json<> > >; _Args = {const std::vector<nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer>, std::allocator<nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer> > >&}; _Tp = std::vector<nlohmann::basic_json<>, std::allocator<nlohmann::basic_json<> > >]'
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/bits/alloc_traits.h:512:17: required from 'static void std::allocator_traits<std::allocator<_Tp1> >::construct(std::allocator_traits<std::allocator<_Tp1> >::allocator_type&, _Up*, _Args&& ...) [with _Up = std::vector<nlohmann::basic_json<>, std::allocator<nlohmann::basic_json<> > >; _Args = {const std::vector<nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer>, std::allocator<nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer> > >&}; _Tp = std::vector<nlohmann::basic_json<>, std::allocator<nlohmann::basic_json<> > >; std::allocator_traits<std::allocator<_Tp1> >::allocator_type = std::allocator<std::vector<nlohmann::basic_json<>, std::allocator<nlohmann::basic_json<> > > >]'
#### json.nlohmann/json.hpp:15381:35: [ skipping 2 instantiation contexts, use -ftemplate-backtrace-limit=0 to disable ]
#### json.nlohmann/json.hpp:16365:25: required from 'nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer>::basic_json(const nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer>&) [with ObjectType = std::map; ArrayType = std::vector; StringType = std::__cxx11::basic_string<char>; BooleanType = bool; NumberIntegerType = long int; NumberUnsignedType = long unsigned int; NumberFloatType = double; AllocatorType = std::allocator; JSONSerializer = nlohmann::adl_serializer]'
#### json.nlohmann/json.hpp:4737:31: required from 'bool nlohmann::detail::json_sax_dom_callback_parser<BasicJsonType>::end_object() [with BasicJsonType = nlohmann::basic_json<>]'
#### json.nlohmann/json.hpp:8847:33: required from 'bool nlohmann::detail::parser<BasicJsonType>::sax_parse_internal(SAX*) [with SAX = nlohmann::detail::json_sax_dom_callback_parser<nlohmann::basic_json<> >; BasicJsonType = nlohmann::basic_json<>]'
#### json.nlohmann/json.hpp:8738:31: required from 'void nlohmann::detail::parser<BasicJsonType>::parse(bool, BasicJsonType&) [with BasicJsonType = nlohmann::basic_json<>]'
#### json.nlohmann/json.hpp:20882:79: required from 'static nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer> nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer>::parse(IteratorType, IteratorType, nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer>::parser_callback_t, bool) [with IteratorType = const char*; typename std::enable_if<std::is_base_of<std::random_access_iterator_tag, typename std::iterator_traits<_InputIterator>::iterator_category>::value, int>::type <anonymous> = 0; ObjectType = std::map; ArrayType = std::vector; StringType = std::__cxx11::basic_string<char>; BooleanType = bool; NumberIntegerType = long int; NumberUnsignedType = long unsigned int; NumberFloatType = double; AllocatorType = std::allocator; JSONSerializer = nlohmann::adl_serializer; nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer>::parser_callback_t = std::function<bool(int, nlohmann::detail::parser<nlohmann::basic_json<> >::parse_event_t, nlohmann::basic_json<>&)>]'
#### json.nlohmann/json.hpp:22700:42: required from here
/opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/bits/stl_uninitialized.h:137:72: error: 'value' is not a member of 'std::is_constructible<nlohmann::basic_json<>, const nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer>&>'
137 | static_assert(is_constructible<_ValueType2, decltype(*__first)>::value,
| ^~~~~
Execution build compiler returned: 1
It seem like it doesn't happen with the develop branch. Here's a compiler explorer like that shows this.
I'm quite unsure what caused this. I'm glad this is fixed now. I would be ready to close this issue unless someone would like to take the time to explain what happened there. I tried to find an explanation but didn't found any.
I neither have a clue nor could reproduce it with the develop branch either...
Was this maybe fixed by #2034 ? I experienced that issue also without including
EDIT: godbolt to prove my point: https://godbolt.org/z/4Pswt6
Left is the version without #2034 merged, right is after the merge.
As I have no clue and this is a nice explanation, I am happy to close this issue. Thanks for digging into this!
I suppose I found the answer to that mind-boggling question. I'm not a compiler expert though so take that into consideration.
First of all, enclosed is the minimal code of single header where I removed everything that wasn't necessary to reproduce the bug:
file _json.hpp_
#include <string>
#include <type_traits>
#include <utility>
template<typename>
class basic_json;
using json = basic_json<std::string>;
class json_ref
{
public:
template <typename T,
bool = std::is_constructible<json, T>::value>
json_ref(T &&){}
};
template<typename>
class basic_json
{
public:
basic_json(json_ref) {}
};
namespace std {
template<>
void swap<json>(json&, json&) noexcept {}
} // namespace std
file _main.cpp_
#include <any>
#include "json.hpp"
int main() {}
Then I started playing with it and I discovered strange relations and how the code could be fixed by:
1) swapping around includes of "json.hpp" and <any>
2) removing #include <any>
3) changing #include <any> to #include <variant>
4) changing using json = basic_json<std::string>; to using json = basic_json<int>;
5) removing bool = std::is_constructible<json, T>::value>
6) chaning json_ref(T &&){} to json_ref(T) / json_ref(const T&) / json_ref(const T&&)
7) moving the json_ref class to the bottom of json.hpp (but only to the bottom, doesn't work between basic_json and namespace std extension)
8) removing the external swap (i.e. extension of std)
9) removing the contructor of basic_json(json_ref) {}
and much more :D
std::swap specialization to std::anyGCC always starts its ranting with the external swap function (our std::swap specialization) so it's clearly the source of the error. This explicit specialization forces checking all other existing swaps to be inspected and eventually leads to the bug.
Now, as I followed a GCC stacktrace, I found that along the chain, it performs some SFINAE of form:
struct __do_is_swappable_impl
{
template<typename _Tp, typename
= decltype(swap(std::declval<_Tp&>(), std::declval<_Tp&>()))>
// (...)
_Note: Clang also reaches this code, but I lose the track of where it goes after_
But from here it "magically" goes to std::any constructor. Why? Check out std::any external swap function signature; it is:
/// Exchange the states of two @c any objects.
inline void swap(any& __x, any& __y) noexcept { __x.swap(__y); }
So, the previously mentioned decltype(swap(std::declval<_Tp&>(), std::declval<_Tp&>()))> considers converting json to std::any implicitly (through one of std::any non-explicit constructors) as a better match than our std::swap
std::any constructor to std::__is_constructible_implAs the compiler goes further, it has to check SFINAE requirements of std::any contructor:
/// Construct with a copy of @p __value as the contained object.
template <typename _Tp, typename _VTp = _Decay_if_not_any<_Tp>,
typename _Mgr = _Manager<_VTp>,
enable_if_t<is_copy_constructible<_VTp>::value
&& !__is_in_place_type<_VTp>::value, bool> = true>
any(_Tp&& __value)
// (...)
It calls the metafunction of std::is_copy_constructible onto our json type which is later dispatched to the _mysterious_ std::__is_constructible_impl.
std::__is_constructible_impl to std::__is_constructible_impl (indirect incompleteness)What we soon get is that std::__is_constructible_impl is incomplete. Why? Because inserting json to that mysterious std::__is_constructible_impl involves inspecting all constructors of json obviously, but since we have such a constructor:
basic_json(json_ref) {}
we need to check constructors of json_ref too (as we depend on that type). Compiler just tries everything, e.g. it looks for some implicit conversions here. The catch here is that the dependent constructor
template <typename T,
bool = std::is_constructible<json, T>::value>
json_ref(T &&){}
instantiates std::is_constructible, but the latter derives from std::__is_constructible_impl.
I mean:
std::__is_constructible_impl depends on basic_json
basic_json depends on json_ref
json_ref depends on std::is_constructible
std::is_constructible depends on std::__is_constructible_impl
IMO it's hard to tell where and what to fix. From user POV it is bollocks that #include <any> rejects the code for seemingly no reason.
Though, I'm gonna be a devil's advocate here and I defend GCC as I reckon that GCC works fine here. IMHO, it's rather the defect of C++17 standard that requires us to write std::swap specializations (at least until C++20) and forbids us from overloading std::swap for our own types. This makes the compiler think that swap<json>(json&, json&) is worse than swap(any&, any&). As we see, this results in convoluted scenarios which take hours of analysis and return no clear error to the user.
Last but not least, unfortunately I have no clue what clang does after inspecting __do_is_swappable_impl. Actually, on my system GCC and Clang share the same header files but they split up in this point. Who knows, maybe clang is more right or wrong here.
Maybe #2176 is related to this.
Thank you for this explanation. This is why I stumbled across the std::swap specialization while investigating.
:)
FYI, I've reported a regression bug in GCC bugzilla as I understand how it behaves but I don't know whether this is how it should be.
Please note the following resolution from Jonathan Wakely on the GCC issue:
The json code is incorrect and should be fixed.
The std::swap
specialization is wrong and should be replaced by a normal (non-template) overload in the same namespace as the json type: void swap(json&, json&) noexcept;The json_ref constructor should be constrained to avoid recursive instantiations with incomplete types, e.g.
template<typename T, typename U> using is_not = std::is_same<std::remove_cv_t<std::remove_reference_t<T>>, U>; class json_ref { public: template <typename T, typename = std::enable_if_t<!is_not<T, json_ref>::value>, typename = std::enable_if_t<!is_not<T, json>::value>, bool = std::is_constructible<json, T>::value> json_ref(T &&){} };
I think I failed to think about a possible "infinite"/incomplete template instantiation here when I wrote the initial version of the json_ref code. Before GCC 10 the stdlib metafunctions did not check that and just returned false, making the code work.
Most helpful comment
I suppose I found the answer to that mind-boggling question. I'm not a compiler expert though so take that into consideration.
MINIMAL CODE EXAMPLE
First of all, enclosed is the minimal code of single header where I removed everything that wasn't necessary to reproduce the bug:
file _json.hpp_
file _main.cpp_
OBSERVATIONS
Then I started playing with it and I discovered strange relations and how the code could be fixed by:
1) swapping around includes of
"json.hpp"and<any>2) removing
#include <any>3) changing
#include <any>to#include <variant>4) changing
using json = basic_json<std::string>;tousing json = basic_json<int>;5) removing
bool = std::is_constructible<json, T>::value>6) chaning
json_ref(T &&){}tojson_ref(T)/json_ref(const T&)/json_ref(const T&&)7) moving the
json_refclass to the bottom of json.hpp (but only to the bottom, doesn't work betweenbasic_jsonand namespace std extension)8) removing the external swap (i.e. extension of std)
9) removing the contructor of
basic_json(json_ref) {}and much more :D
ANALYSIS AND EXPLANATION
From
std::swapspecialization tostd::anyGCC always starts its ranting with the external swap function (our
std::swapspecialization) so it's clearly the source of the error. This explicit specialization forces checking all other existing swaps to be inspected and eventually leads to the bug.Now, as I followed a GCC stacktrace, I found that along the chain, it performs some SFINAE of form:
_Note: Clang also reaches this code, but I lose the track of where it goes after_
But from here it "magically" goes to
std::anyconstructor. Why? Check outstd::anyexternal swap function signature; it is:So, the previously mentioned specialization.
decltype(swap(std::declval<_Tp&>(), std::declval<_Tp&>()))>considers convertingjsontostd::anyimplicitly (through one ofstd::anynon-explicit constructors) as a better match than our std::swapFrom
std::anyconstructor tostd::__is_constructible_implAs the compiler goes further, it has to check SFINAE requirements of
std::anycontructor:It calls the metafunction of
std::is_copy_constructibleonto ourjsontype which is later dispatched to the _mysterious_std::__is_constructible_impl.From
std::__is_constructible_impltostd::__is_constructible_impl(indirect incompleteness)What we soon get is that
std::__is_constructible_implis incomplete. Why? Because insertingjsonto that mysteriousstd::__is_constructible_implinvolves inspecting all constructors ofjsonobviously, but since we have such a constructor:we need to check constructors of json_ref too (as we depend on that type). Compiler just tries everything, e.g. it looks for some implicit conversions here. The catch here is that the dependent constructor
instantiates
std::is_constructible, but the latter derives fromstd::__is_constructible_impl.I mean:
std::__is_constructible_impldepends onbasic_jsonbasic_jsondepends onjson_refjson_refdepends onstd::is_constructiblestd::is_constructibledepends onstd::__is_constructible_implAFTERWORD
IMO it's hard to tell where and what to fix. From user POV it is bollocks that
#include <any>rejects the code for seemingly no reason.Though, I'm gonna be a devil's advocate here and I defend GCC as I reckon that GCC works fine here. IMHO, it's rather the defect of C++17 standard that requires us to write
std::swapspecializations (at least until C++20) and forbids us from overloadingstd::swapfor our own types. This makes the compiler think thatswap<json>(json&, json&)is worse thanswap(any&, any&). As we see, this results in convoluted scenarios which take hours of analysis and return no clear error to the user.Last but not least, unfortunately I have no clue what clang does after inspecting
__do_is_swappable_impl. Actually, on my system GCC and Clang share the same header files but they split up in this point. Who knows, maybe clang is more right or wrong here.