The standard explicitly requires the <ios> header to include <iosfwd>,
which has declarations of all the standard stream buffers and stream
types. Because we include <ios> in <istream> and <ostream>, this means
that <iosfwd> is included everywhere, and so every iostream header has a
forward declaration of every iostream type. This means that for example,
<fstream> has a declaration (but not the definition) of std::stringbuf.

This leads to a poor user experience, because the compiler's fixit hints
for undeclared types do not trigger if the type _has_ been declared,
instead users get an error about using an incomplete type. See the
example in PR 125371, where using std::istringstream after including
<fstream> fails to suggest including <sstream>.

If we stop including <ios> in <istream> and <ostream>, and instead
include _most_ of the same things that <ios> provides, then we can avoid
the unhelpful declarations of the entire family of iostream types in
every header. Users who really do want a declaration of std::filebuf
or std::istringstream (but don't want the full definition) can still
explicitly include <iosfwd> to get those declarations. But they won't
get them as a side effect of <fstream> etc.

We need to explicitly include <ios> in <iostream>.  The standard
requires it there, and after this change we no longer get it via
<istream> and <ostream>.

libstdc++-v3/ChangeLog:

        PR libstdc++/125371
        * include/Makefile.am: Add new bits/iosfwd.h header.
        * include/Makefile.in: Regenerate.
        * include/bits/ostream.h: Replace <ios> with its constituent
        parts, except for <iosfwd>.
        * include/std/ios: Do not include <exception> or
        <bits/char_traits.h>.
        * include/std/iosfwd (ios_base, basic_ios, basic_streambuf)
        (basic_istream, basic_ostream, basic_iostream): Move
        declarations to new header.
        * include/std/iostream: Include <ios>.
        * include/std/istream: Replace <ios> with its constituent
        parts, except for <iosfwd>.
        * include/bits/iosfwd.h: New file.
---

Tested x86_64-linux.

Does this look like something we should do?

 libstdc++-v3/include/Makefile.am    |  1 +
 libstdc++-v3/include/Makefile.in    |  1 +
 libstdc++-v3/include/bits/iosfwd.h  | 96 +++++++++++++++++++++++++++++
 libstdc++-v3/include/bits/ostream.h |  7 ++-
 libstdc++-v3/include/std/ios        |  2 -
 libstdc++-v3/include/std/iosfwd     | 47 +-------------
 libstdc++-v3/include/std/iostream   |  2 +-
 libstdc++-v3/include/std/istream    |  8 ++-
 8 files changed, 114 insertions(+), 50 deletions(-)
 create mode 100644 libstdc++-v3/include/bits/iosfwd.h

diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
index ec932181a966..fd660f7ef30e 100644
--- a/libstdc++-v3/include/Makefile.am
+++ b/libstdc++-v3/include/Makefile.am
@@ -140,6 +140,7 @@ bits_freestanding = \
        ${bits_srcdir}/functional_hash.h \
        ${bits_srcdir}/intcmp.h \
        ${bits_srcdir}/invoke.h \
+       ${bits_srcdir}/iosfwd.h \
        ${bits_srcdir}/iterator_concepts.h \
        ${bits_srcdir}/new_except.h \
        ${bits_srcdir}/new_throw.h \
diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in
index 00ae5209f604..275cb06bd6aa 100644
--- a/libstdc++-v3/include/Makefile.in
+++ b/libstdc++-v3/include/Makefile.in
@@ -498,6 +498,7 @@ bits_freestanding = \
        ${bits_srcdir}/functional_hash.h \
        ${bits_srcdir}/intcmp.h \
        ${bits_srcdir}/invoke.h \
+       ${bits_srcdir}/iosfwd.h \
        ${bits_srcdir}/iterator_concepts.h \
        ${bits_srcdir}/new_except.h \
        ${bits_srcdir}/new_throw.h \
diff --git a/libstdc++-v3/include/bits/iosfwd.h 
b/libstdc++-v3/include/bits/iosfwd.h
new file mode 100644
index 000000000000..d43bd9775f38
--- /dev/null
+++ b/libstdc++-v3/include/bits/iosfwd.h
@@ -0,0 +1,96 @@
+// <iosfwd> Forward declarations -*- C++ -*-
+
+// Copyright (C) 1997-2026 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+// <http://www.gnu.org/licenses/>.
+
+/** @file include/iosfwd
+ *  This is an internal header file, included by other library headers.
+ *  Do not attempt to use it directly. @headername{iosfwd}
+ */
+
+#ifndef _GLIBCXX_IOSFWD_H
+#define _GLIBCXX_IOSFWD_H 1
+
+#ifdef _GLIBCXX_SYSHDR
+#pragma GCC system_header
+#endif
+
+#include <bits/requires_hosted.h> // iostreams
+
+#include <bits/c++config.h>
+#include <bits/char_traits.h>  // For char_traits, streamoff, streamsize, fpos
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+  /**
+   *  @defgroup io I/O
+   *
+   *  Nearly all of the I/O classes are parameterized on the type of
+   *  characters they read and write.  (The major exception is ios_base at
+   *  the top of the hierarchy.)  This is a change from pre-Standard
+   *  streams, which were not templates.
+   *
+   *  For ease of use and compatibility, all of the basic_* I/O-related
+   *  classes are given typedef names for both of the builtin character
+   *  widths (wide and narrow).  The typedefs are the same as the
+   *  pre-Standard names, for example:
+   *
+   *  @code
+   *     typedef basic_ifstream<char>  ifstream;
+   *  @endcode
+   *
+   *  Because properly forward-declaring these classes can be difficult, you
+   *  should not do it yourself.  Instead, include the &lt;iosfwd&gt;
+   *  header, which contains only declarations of all the I/O classes as
+   *  well as the typedefs.  Trying to forward-declare the typedefs
+   *  themselves (e.g., <code>class ostream;</code>) is not valid ISO C++.
+   *
+   *  For more specific declarations, see
+   *  https://gcc.gnu.org/onlinedocs/libstdc++/manual/io.html#std.io.objects
+   *
+   *  @{
+  */
+  class ios_base;
+
+  template<typename _CharT, typename _Traits = char_traits<_CharT> >
+    class basic_ios;
+
+  template<typename _CharT, typename _Traits = char_traits<_CharT> >
+    class basic_streambuf;
+
+  template<typename _CharT, typename _Traits = char_traits<_CharT> >
+    class basic_istream;
+
+  template<typename _CharT, typename _Traits = char_traits<_CharT> >
+    class basic_ostream;
+
+  template<typename _CharT, typename _Traits = char_traits<_CharT> >
+    class basic_iostream;
+
+  /** @}  */
+
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace
+
+#endif /* _GLIBCXX_IOSFWD_H */
diff --git a/libstdc++-v3/include/bits/ostream.h 
b/libstdc++-v3/include/bits/ostream.h
index 7c53b21bdb3d..703ebf7890cd 100644
--- a/libstdc++-v3/include/bits/ostream.h
+++ b/libstdc++-v3/include/bits/ostream.h
@@ -40,7 +40,12 @@
 
 #include <bits/requires_hosted.h> // iostreams
 
-#include <ios>
+#include <bits/iosfwd.h>       // For declarations of default template args.
+#include <bits/char_traits.h>  // For char_traits, streamoff, streamsize, fpos
+#include <bits/localefwd.h>    // For class locale
+#include <bits/ios_base.h>     // For ios_base declarations.
+#include <streambuf>
+#include <bits/basic_ios.h>
 #include <bits/ostream_insert.h>
 
 # define __glibcxx_want_print
diff --git a/libstdc++-v3/include/std/ios b/libstdc++-v3/include/std/ios
index 49f3aafd1de0..e9b1d6a165cd 100644
--- a/libstdc++-v3/include/std/ios
+++ b/libstdc++-v3/include/std/ios
@@ -40,8 +40,6 @@
 #include <bits/requires_hosted.h> // iostreams
 
 #include <iosfwd>
-#include <exception>           // For ios_base::failure
-#include <bits/char_traits.h>  // For char_traits, streamoff, streamsize, fpos
 #include <bits/localefwd.h>    // For class locale
 #include <bits/ios_base.h>     // For ios_base declarations.
 #include <streambuf>
diff --git a/libstdc++-v3/include/std/iosfwd b/libstdc++-v3/include/std/iosfwd
index 42124ad30df4..4ee4c2648490 100644
--- a/libstdc++-v3/include/std/iosfwd
+++ b/libstdc++-v3/include/std/iosfwd
@@ -39,60 +39,17 @@
 
 #include <bits/requires_hosted.h> // iostreams
 
-#include <bits/c++config.h>
+#include <bits/iosfwd.h>
 #include <bits/stringfwd.h>    // For string forward declarations.
-#include <bits/postypes.h>
 
 namespace std _GLIBCXX_VISIBILITY(default)
 {
 _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   /**
-   *  @defgroup io I/O
-   *
-   *  Nearly all of the I/O classes are parameterized on the type of
-   *  characters they read and write.  (The major exception is ios_base at
-   *  the top of the hierarchy.)  This is a change from pre-Standard
-   *  streams, which were not templates.
-   *
-   *  For ease of use and compatibility, all of the basic_* I/O-related
-   *  classes are given typedef names for both of the builtin character
-   *  widths (wide and narrow).  The typedefs are the same as the
-   *  pre-Standard names, for example:
-   *
-   *  @code
-   *     typedef basic_ifstream<char>  ifstream;
-   *  @endcode
-   *
-   *  Because properly forward-declaring these classes can be difficult, you
-   *  should not do it yourself.  Instead, include the &lt;iosfwd&gt;
-   *  header, which contains only declarations of all the I/O classes as
-   *  well as the typedefs.  Trying to forward-declare the typedefs
-   *  themselves (e.g., <code>class ostream;</code>) is not valid ISO C++.
-   *
-   *  For more specific declarations, see
-   *  https://gcc.gnu.org/onlinedocs/libstdc++/manual/io.html#std.io.objects
-   *
+   *  @ingroup io
    *  @{
   */
-  class ios_base;
-
-  template<typename _CharT, typename _Traits = char_traits<_CharT> >
-    class basic_ios;
-
-  template<typename _CharT, typename _Traits = char_traits<_CharT> >
-    class basic_streambuf;
-
-  template<typename _CharT, typename _Traits = char_traits<_CharT> >
-    class basic_istream;
-
-  template<typename _CharT, typename _Traits = char_traits<_CharT> >
-    class basic_ostream;
-
-  template<typename _CharT, typename _Traits = char_traits<_CharT> >
-    class basic_iostream;
-
-
 _GLIBCXX_BEGIN_NAMESPACE_CXX11
 
   template<typename _CharT, typename _Traits = char_traits<_CharT>,
diff --git a/libstdc++-v3/include/std/iostream 
b/libstdc++-v3/include/std/iostream
index f4b5fd868346..4b97dd2951ae 100644
--- a/libstdc++-v3/include/std/iostream
+++ b/libstdc++-v3/include/std/iostream
@@ -39,7 +39,7 @@
 
 #include <bits/requires_hosted.h> // iostreams
 
-#include <bits/c++config.h>
+#include <ios>
 #include <ostream>
 #include <istream>
 
diff --git a/libstdc++-v3/include/std/istream b/libstdc++-v3/include/std/istream
index 6a982460a1dc..9b8474a0da8d 100644
--- a/libstdc++-v3/include/std/istream
+++ b/libstdc++-v3/include/std/istream
@@ -39,7 +39,13 @@
 
 #include <bits/requires_hosted.h> // iostreams
 
-#include <ios>
+#include <bits/iosfwd.h>       // For declarations of default template args.
+#include <bits/char_traits.h>  // For char_traits, streamoff, streamsize, fpos
+#include <bits/localefwd.h>    // For class locale
+#include <bits/ios_base.h>     // For ios_base declarations.
+#include <streambuf>
+#include <bits/basic_ios.h>
+
 #include <ostream>
 
 #if __cplusplus > 202302L
-- 
2.54.0

Reply via email to