This is the mail archive of the libstdc++@gcc.gnu.org mailing list for the libstdc++ project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

RFC: Bug 92285 - Layout of istreambuf_iterator subobject depends on -std mode


I've just noticed https://gcc.gnu.org/PR92285 which shows that this
program prints a different result in C++98 vs other modes:

#include <iterator>
#include <iostream>

struct I : std::iterator<std::input_iterator_tag, char>
{ };

struct J : I, std::istreambuf_iterator<char>
{ };

int main()
{
 std::cout << sizeof(J) << '\n';
}

For C++98 it prints 24 but for other modes it prints 16. The reason is
that std::istreambuf_iterator has a different base class:

 template<typename _CharT, typename _Traits>
   class istreambuf_iterator
   : public iterator<input_iterator_tag, _CharT, typename _Traits::off_type,
		      _CharT*,
#if __cplusplus >= 201103L
   // LWG 445.
		      _CharT>
#else
		      _CharT&>
#endif

This affects layout because std::iterator is an empty class, so
whether the two base classes can share the same address depends on
what istreambuf_iterator's base class is.

This isn't a disaster, because in practice it is probably very rare
for a type to have two std::iterator subobjects that could have the
same address. But technically it's still an ABI incompatibility
between C++98 and C++11/14/17 modes.

The solution is to make istreambuf_iterator always have the same base
class, but then conditionally override the reference member:

 template<typename _CharT, typename _Traits>
   class istreambuf_iterator
   : public iterator<input_iterator_tag, _CharT, typename _Traits::off_type,
		      _CharT*, ???>
   {
   public:
     using reference = ???;

Now the base class will always be the same, and so won't change layout
when __cplusplus changes.

We have two options:

1) Make the base class the same as it was in C++98, and override it
for other modes.  The program above would always print 24.  This makes
the layout always consistent with the GCC 4.6 layout, for all modes,
but incompatible with the default -std=gnu++14 mode since GCC 6 (and
incompatible with code explicitly using C++11 or C++14 in GCC 4.7 and
above).

Or:

2) Always use the new base class, and override it for C++98.  The
program above would always print 16.  This makes layout for C++98
compatible with the current -std=gnu++14 default, but no longer
compatible with C++98 code from previous releases.

Option 1 might be better for users of older enterprise distros, who are
still using GCC 4.x compilers and C++98 (because it's the default, and
possibly the only mode supported by their vendor, e.g. RHEL 7 users
using the system GCC).

Option 2 is probably better for everybody using recent versions of GCC
and recent standard modes. They probably don't care about C++98 (or
GCC 4.6) any more, and so always using the new layout won't affect
them.

I think I'm leaning towards option 2. Nobody has noticed this, so I
don't expect it to affect anybody if we change C++98 mode now to match
the C++14 default.

N.B. the decision should not be "choose the option that makes
sizeof(J) smaller", because I can easily create an example where C++98
prints 16 and other modes print 24, so we're not trying to optimize
for size. What matters is compatibility, not the absolute value.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]