Date: Mon, 17 Mar 2025 14:38:15 -0400
The clean and simple solution is to never forward declare an entity (a type, a function, a template, etc) that comes from another library (including the std library).
The problem with forward declaration is that it requires us to guess about the implementation details of the entity which is forward declared. In other words, it requires us to make assumptions that goes beyond what is exposed by an API. For instance an API could expose a function which is callable with a specific signature, but this function's declaration could contain additional "hidden" default arguments, or it could in fact be declared as function template with default template arguments. Similarly, an API could expose a type and its associated operations; this type could be declared as a plain class, but it could also be declared as an alias to a class template specialization with template arguments naming a type or value which is "internal" to the API. Another thing to consider is the exact scope where the actual declaration of the entity occurs versus the scope where the name is exposed through the API: the name could be brought into a namespace through a using declaration; the source namespace could itself be "internal" to the library and thus shouldn't be named from the outside.
To forward declare an entity from another library, we need to "guess" the proper declaration of this entity (and also know the proper namespace to place it). Even if we guess right today, the library could eventually make API compatible changes that makes the forward declaration incompatible.
I would even go a step further and say we should not even forward declare across header files from the same library: any change to the original declaration of an entity will most likely break every forward declaration of that entity. If you control the sou
If you really want to forward declare types, there is an alternative solution in case you have control of the source code of a library that exposes the type you want to forward declare. Types you want to forward declare should be placed in (one or more) dedicated forwarding headers within this library. A header providing a type definition for a forward declared type must include the appropriate forwarding header to ensure both the forward declaration and the type definition are consistent. Consumers of the library may then include the appropriate forwarding header if they do not want the forward declaration. Doing it this way ensures that the implementation details of the forward declarations are kept within the library and in a single header.
On March 17, 2025 9:13:20 a.m. EDT, Donald Duck via Std-Proposals <std-proposals_at_[hidden]> wrote:
>Some libraries such as https://github.com/nlohmann/json have classes which
>are in fact typedefs for very complicated types, for example:
>
>template<template<typename U, typename V, typename... Args> class
>> ObjectType =
>> std::map,
>> template<typename U, typename... Args> class ArrayType =
>> std::vector,
>> class StringType = std::string, class BooleanType = bool,
>> class NumberIntegerType = std::int64_t,
>> class NumberUnsignedType = std::uint64_t,
>> class NumberFloatType = double,
>> template<typename U> class AllocatorType = std::allocator,
>> template<typename T, typename SFINAE = void> class JSONSerializer
>> =
>> adl_serializer,
>> class BinaryType = std::vector<std::uint8_t>,
>> class CustomBaseClass = void>
>> class basic_json;
>>
>using json = basic_json<>;
>
>
>I would like to forward declare their json class like this:
>
>class json;
>
>
>The problem is that if I try doing this, I get the following error:
>
>nlohmann/json.hpp: error: conflicting declaration ‘using json = class
>> basic_json<>’
>>
>main.cpp: note: previous declaration as ‘class json’
>
>
>I don't see why this should be an error. class json; declares an incomplete
>type called json and I can't access its members, instantiate it, etc anyway
>unless I include its definition. So it shouldn't matter if it's a class, a
>typedef for a class, or even a typedef for a primitive.
>
>So I suggest that class json; simply means that I'm forward declaring an
>incomplete type that can be anything, instead of meaning that it *must* be
>a class (as opposed to a typedef for a class). Alternatively, a new syntax
>such as typename json; could also work, as long as I don't need to copy the
>entire declaration for their complicated template.
The problem with forward declaration is that it requires us to guess about the implementation details of the entity which is forward declared. In other words, it requires us to make assumptions that goes beyond what is exposed by an API. For instance an API could expose a function which is callable with a specific signature, but this function's declaration could contain additional "hidden" default arguments, or it could in fact be declared as function template with default template arguments. Similarly, an API could expose a type and its associated operations; this type could be declared as a plain class, but it could also be declared as an alias to a class template specialization with template arguments naming a type or value which is "internal" to the API. Another thing to consider is the exact scope where the actual declaration of the entity occurs versus the scope where the name is exposed through the API: the name could be brought into a namespace through a using declaration; the source namespace could itself be "internal" to the library and thus shouldn't be named from the outside.
To forward declare an entity from another library, we need to "guess" the proper declaration of this entity (and also know the proper namespace to place it). Even if we guess right today, the library could eventually make API compatible changes that makes the forward declaration incompatible.
I would even go a step further and say we should not even forward declare across header files from the same library: any change to the original declaration of an entity will most likely break every forward declaration of that entity. If you control the sou
If you really want to forward declare types, there is an alternative solution in case you have control of the source code of a library that exposes the type you want to forward declare. Types you want to forward declare should be placed in (one or more) dedicated forwarding headers within this library. A header providing a type definition for a forward declared type must include the appropriate forwarding header to ensure both the forward declaration and the type definition are consistent. Consumers of the library may then include the appropriate forwarding header if they do not want the forward declaration. Doing it this way ensures that the implementation details of the forward declarations are kept within the library and in a single header.
On March 17, 2025 9:13:20 a.m. EDT, Donald Duck via Std-Proposals <std-proposals_at_[hidden]> wrote:
>Some libraries such as https://github.com/nlohmann/json have classes which
>are in fact typedefs for very complicated types, for example:
>
>template<template<typename U, typename V, typename... Args> class
>> ObjectType =
>> std::map,
>> template<typename U, typename... Args> class ArrayType =
>> std::vector,
>> class StringType = std::string, class BooleanType = bool,
>> class NumberIntegerType = std::int64_t,
>> class NumberUnsignedType = std::uint64_t,
>> class NumberFloatType = double,
>> template<typename U> class AllocatorType = std::allocator,
>> template<typename T, typename SFINAE = void> class JSONSerializer
>> =
>> adl_serializer,
>> class BinaryType = std::vector<std::uint8_t>,
>> class CustomBaseClass = void>
>> class basic_json;
>>
>using json = basic_json<>;
>
>
>I would like to forward declare their json class like this:
>
>class json;
>
>
>The problem is that if I try doing this, I get the following error:
>
>nlohmann/json.hpp: error: conflicting declaration ‘using json = class
>> basic_json<>’
>>
>main.cpp: note: previous declaration as ‘class json’
>
>
>I don't see why this should be an error. class json; declares an incomplete
>type called json and I can't access its members, instantiate it, etc anyway
>unless I include its definition. So it shouldn't matter if it's a class, a
>typedef for a class, or even a typedef for a primitive.
>
>So I suggest that class json; simply means that I'm forward declaring an
>incomplete type that can be anything, instead of meaning that it *must* be
>a class (as opposed to a typedef for a class). Alternatively, a new syntax
>such as typename json; could also work, as long as I don't need to copy the
>entire declaration for their complicated template.
Received on 2025-03-17 18:38:25