-----Original Message-----
From: Andrew Pinski <pins...@gmail.com>
Sent: Friday, July 12, 2024 1:20 PM
To: Dalbey, Keith <kdal...@sandia.gov>
Cc: Jonathan Wakely <jwakely....@gmail.com>; gcc@gcc.gnu.org
Subject: Re: [EXTERNAL] Re: g++12 broke my system of overloaded operator<<

On Fri, Jul 12, 2024 at 12:10 PM Dalbey, Keith via Gcc <gcc@gcc.gnu.org> wrote:
>
> The means by which this FIX was implemented caused OTHER problems
>
> template <typename F, typename S>
> std::ostream& operator<<(std::ostream& os, const std::pair<F,S>& pr) {
>         os << "(" << pr.first << ", " << pr.second << ")";
>         return os;
> }


You could just do:
```
template <typename F, typename S>
std::ostream& operator<<(std::ostream& os, const std::pair<F,S>& pr); ....
//include all other headers
template <typename F, typename S>
std::ostream& operator<<(std::ostream& os, const std::pair<F,S>& pr) {
         os << "(" << pr.first << ", " << pr.second << ")";
         return os;
}
```
And then it will work correctly.
But now this is getting into C++ help rather than GCC help really.

Thanks,
Andrew

From Keith: that's effectively what I worked out in the paragraph below from my 
previous email (although I likely said it less clearly) but the issue with the 
solution as you wrote it means not putting all the STL template operator<< in a 
common package/library that gets used by multiple projects, UNLESS you're VERY 
VERY careful to never include the templated operator<< DEFINITIONS header file 
in *OR BEFORE* other headers.   Yes it's workable, but users will encounter 
headaches/confusion when they invariably include the file with the templated 
operator<< definitions IN *OR BEFORE* other header files.  I am not an expert 
on compilers and my practical knowledge of what templates "are" is **how** they 
have **behaved** in the past. I have 3 decades experience programming, almost 
2.5 decades of that in C++ and I've almost exclusively using gcc (so that 
different compilers behaving differently wasn't ever observed) and before 
yesterday I'd never seen or even heard anyone mention using forward 
declarations of templated functions, I honestly didn't know it was a thing.  
Admittedly my PhD is in mechanical engineering and I've using been using gcc to 
solve math/physics problems such as computational fluid dynamics, orbital 
problems, geolocation, statistics, etc. rather than "pure CS problems" like 
databases.  I don't have a CS degree but I do know that programming is/was 
taught very differently to engineering vs CS majors (back when I was first 
learning C++, engineers weren't taught about maps or sets, etc. and shared_ptrs 
weren't a thing yet, I worked out how the STL behaved by using it).  A typical 
engineers perspective on C++ vs C was (and probably still is) new & delete vs 
calloc/malloc/free, vectors instead of arrays; cin, cout, instead of fprintf 
and fscanf; using inline functions instead of macros; and classes are just 
non-public structs with methods and inheritance/polymorphism.  We were told 
that this thing called template meta programming existed and was code that 
executed in the compiler rather than runtime but we didn't actually touch 
templates in college and we weren't really taught about namespaces other than 
std either.   The point I'm making is: most g++ users' mental model of HOW 
templates are SUPPOSED to work probably doesn't match that of the developers of 
the compiler and the reason for that is how it's SUPPOSED to behave is VERY 
POORLY documented particularly on the internet (there's probably some very good 
book on it that CS majors learn C++ from).  That link I found and shared in my 
original email was the **ONLY** thing *relevant* I found in 2 hours of 
googling, but at least I immediately realized it explained **WHY** my code that 
had been working for years without error suddenly stopped working with the 
compiler switch (even though the link didn't directly speak templated 
operators). Thank you, Andrew for showing some understanding for how I reacted 
to having a (mandated) compiler change suddenly pull the rug out from under my 
code (which is being used in multiple projects).  I know a lot of other people 
in my department, most of us are either engineers or CS majors who are now 
responsible for maintaining inherited code written by engineers decades ago.  
And most of us are a bit stressed about needing to update code that broke with 
gcc 12, we don't have a lot of time to do it, and we don't have an option not 
to upgrade to gcc 12.  I'm lucky in that at least I'm maintaining my own code.


> Will only work for CONCRETE classes that take the place of "F" and "S" IFF 
> each of their concrete operator<< 's is FORWARD DECLARED ahead of the above 
> template, so primitives like int and double should still work,  but if you 
> include this file in a header file that contains a concrete class that you 
> define an operator<< for, and then use in another file, you're SOL.
>
> If you mix that with templated operator<< for vectors and
> std::shared_ptr  then there's a chicken and the egg problem for which
> STL templated operator<< gets declared first/ but you can work around
> this by FORWARD declaring all of your operator<< (including the
> concrete and templated ones) before **Defining** the STL templated
> operator<< 's, but that means the header file containing the STL
> templated operator<< 's can never be included in another header file
> and it can only be included as *the absolute last header file* in a
> .cpp file (so that all the concrete operator<< get declared before
> them, and you may still need to forward declare some of the contents
> of the .cpp file ahead of including this header file)
>
> But really a header file that can't be included in other header files
> and can only be included as the absolute last header file in a .cpp
> file is a unreasonable set of hoops to jump through to get TEMPLATED
> operator<< 's to work, which should just work BECAUSE THEY'RE
> TEMPLATES
>
> -----Original Message-----
> From: Jonathan Wakely <jwakely....@gmail.com>
> Sent: Friday, July 12, 2024 12:37 PM
> To: Dalbey, Keith <kdal...@sandia.gov>
> Cc: gcc@gcc.gnu.org
> Subject: [EXTERNAL] Re: g++12 broke my system of overloaded operator<<
>
> [You don't often get email from jwakely....@gmail.com. Learn why this
> is important at https://aka.ms/LearnAboutSenderIdentification ]
>
> On Fri, 12 Jul 2024 at 01:03, Dalbey, Keith via Gcc <gcc@gcc.gnu.org> wrote:
> >
> > So I'm on redhat 7 and just got devtoolsset-12 and code (a system of
> > overloaded<< operators) that was working with devtoolset-10 now
> > break (because of ordering)
> >
> > To not bury the lead..
> >
> > My code relies on the version 11 or older behavior (see below), and I don't 
> > see how anyone could call the new behavior an improvement or correction 
> > because it neuters/cancels out  the power/flexibility of the STL.   Yes one 
> > could technically work around it by forward declaring templated operator<<  
> > but that makes the system not extensible,  a common package/gitlab project 
> > that handles this for the STL and then gets succeed into another library 
> > that overloads the operator<< for concrete classes just doesn't work any 
> > more... and that was my exact use case.
> >
> > Please reverse this change in future editions of gcc, it is absolutely 
> > awful.
>
> Why would you want the example below to behave differently for
> operator+(t, 0) and t+0 when they're two different ways to spell the
> same function call?
>
> The old behaviour was clearly a bug, and thankfully has been fixed.
> See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=51577 for the bug report I 
> made about it years ago, which has a large number of duplicates because other 
> people also reported the old behaviour as a bug. Because it was a bug, and 
> people wanted GCC to follow the C++ standard.
>
> I'm sorry you've written code that depends on a bug, but we're not going to 
> break GCC again to restore the bug.
>
>
> >
> > From this link
> > https://deve/
> > lopers.redhat.com%2Farticles%2F2022%2F04%2F25%2Fnew-c-features-gcc-1
> > 2%
> > 23corrections_and_internal_improvements&data=05%7C02%7Ckdalbey%40san
> > di
> > a.gov%7Cc1e76e1166844e639bc208dca2a1aa33%7C7ccb5a20a303498cb0c129007
> > 38
> > 1b574%7C1%7C0%7C638564062475549377%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiM
> > C4
> > wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C0%7C%7C%7C
> > &s
> > data=KxNgv9u%2BDq8toLCNArhF1xjc1tHoP2Dy%2BYEv5P2gMXo%3D&reserved=0
> > Corrections and internal improvements The changes described in this
> > section bring GCC more in line with recent changes to the standard, and 
> > permit behavior that previously did not work correctly.
> > Dependent operator lookup changes
> > GCC 12 corrected a problem where the compiler performed an unqualified 
> > lookup for a dependent operator expression at template definition time 
> > instead of at instantiation time. The fix matches the existing behavior for 
> > dependent call expressions. Consider the following test case demonstrating 
> > this change:
> > #include <iostream>
> >
> > namespace N {
> >   struct A { };
> > }
> >
> > void operator+(N::A, double) {
> >   std::cout << "#1 ";
> > }
> >
> > template<class T>
> > void f(T t) {
> >   operator+(t, 0);
> >   t + 0;
> > }
> >
> > // Since it's not visible from the template definition, this
> > later-declared // operator overload should not be considered when
> > instantiating f<N::A>(N::A), // for either the call or operator expression.
> > void operator+(N::A, int) {
> >   std::cout << "#2 ";
> > }
> >
> > int main() {
> >   N::A a;
> >   f(a);
> >   std::cout << std::endl;
> > }
> > Copy snippet
> > This program will print #1 #2 when compiled with versions 11 or older of 
> > GCC, but GCC 12 correctly prints #1 #1. That's because previously only the 
> > call expression resolved to the #1 overload, but with GCC 12 the operator 
> > expression does too.

Reply via email to