G++ on MacOsX acts different when enabling the new c++11 related to operator new overloads. If I compile the following code: #include "stdlib.h" #include <new> void* operator new(size_t mem) throw(std::bad_alloc); void* operator new(size_t mem) throw(std::bad_alloc); Then when compiling g++ new.cpp, it compiles fine. But when I compile g++ -std=c++11 new.cpp, then it results in this error: new.cpp:6:52: error: declaration of ‘void* operator new(size_t) throw (std::bad_alloc)’ has a different exception specifier void* operator new(size_t mem) throw(std::bad_alloc); ^ new.cpp:5:7: error: from previous declaration ‘void* operator new(std::size_t)’ void* operator new(size_t mem) throw(std::bad_alloc); ^ ------------ I've not been able to replicate this under linux and digging into it a bit, it seems to relate to the c++config.h definitions of _GLIBCXX_THROW.
This happens on Linux too. The issue is that per C++11 (see 18.6) operator new is declared in <new> as: void* operator new(size_t mem); (1) and as such is internally pre-declared in decl.c:cxx_init_decl_processing. Then, if I understand correctly, when the parser sees in user code: void* operator new(size_t mem) throw(std::bad_alloc); it thinks, Ok the user is just redeclaring (1), it simply ignores the exception specifier. Then the additional declaration in user code is seen inconsistent with the former one (it becomes clear that the exception specifier was dropped the first time). We (used to) have a completely similar, dual, issue in C++98 for this user code: void* operator new(std::size_t mem); void* operator new(std::size_t mem); and I'm not sure whether and how we want to do better. Note that changing in C++11 the user code to: void* operator new(std::size_t mem); void* operator new(std::size_t mem); is perfectly fine.
There exists a related core language issue for this: http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#967 Originally it was submitted when these function had an exception specification, but in C++11 the situation is different. Nonetheless I believe that this issue might influence whether this will turn out to be a gcc bug or not.
Thanks for the comments. I understand the problems in implementing a compiler, when this is also unclear in the language itself. Whatever is decided related to this, it would probably be a good idea to give a better error message. Right now, the error message is telling the user that the operator new *with* an exception specifier doesn't have one and that they are different, even though the two lines are exactly the same. It is a bit confusing. It would be good though to solve this in a way that both works with C++11 and C++03 as it came up when compiling a piece of code that is used by multiple compilers and by both language versions.
So the defect report against C++ was closed as not a defect: http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_closed.html#967 . The related one http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_closed.html#1948 dealing with noexcept was also closed as not a defect. So GCC is correct, There is no bug here.
I agree with comment 3 that the current diagnostic is poor: #include <new> void* operator new(std::size_t mem) throw(std::bad_alloc); void* operator new(std::size_t mem) throw(std::bad_alloc); g++ -std=c++11 -c new.cc -Wno-deprecated new.cc:3:7: error: declaration of ‘void* operator new(std::size_t) throw (std::bad_alloc)’ has a different exception specifier 3 | void* operator new(std::size_t mem) throw(std::bad_alloc); | ^~~~~~~~ new.cc:2:7: note: from previous declaration ‘void* operator new(std::size_t)’ 2 | void* operator new(std::size_t mem) throw(std::bad_alloc); | ^~~~~~~~ Why does it accept the incorrect exception specification on line 2, but then give an error for an identical one on line 3? Why do we refer to line 2 in the note and say it's different, when it's not? Paolo's explanation in comment 1 doesn't make the behaviour correct, it just explains why we behave like that. Either both redeclarations should be valid or neither should be valid. So I think either we should either: - accept both redeclarations (as Clang does), or - give a diagnostic (maybe a pedwarn) for the first redeclaration because it doesn't match the one in <new> (and the implicit one predefined by the compiler), and fix the diagnostic to refer to the previous declaration in <new> instead of the one on line 2.
(In reply to Jonathan Wakely from comment #5) > I agree with comment 3 that the current diagnostic is poor: Oh I missed that. > > #include <new> > void* operator new(std::size_t mem) throw(std::bad_alloc); > void* operator new(std::size_t mem) throw(std::bad_alloc); > > > g++ -std=c++11 -c new.cc -Wno-deprecated > new.cc:3:7: error: declaration of ‘void* operator new(std::size_t) throw > (std::bad_alloc)’ has a different exception specifier > 3 | void* operator new(std::size_t mem) throw(std::bad_alloc); > | ^~~~~~~~ > new.cc:2:7: note: from previous declaration ‘void* operator new(std::size_t)’ > 2 | void* operator new(std::size_t mem) throw(std::bad_alloc); > | ^~~~~~~~ > Reduced testcase for that: void f(void); void f(void) throw(int); void f(void) throw(int); Noexcept has the same issue: void g(void); void g(void) noexcept; void g(void) noexcept; I suspect what happens is the following when we merge the two decls we chose the new decl for the location but we remove the exception specifier/noexcept (confirmed by swapping the first two decls and seeing the error again).
(In reply to Andrew Pinski from comment #6) > Reduced testcase for that: > void f(void); > void f(void) throw(int); > void f(void) throw(int); > > Noexcept has the same issue: > void g(void); > void g(void) noexcept; > void g(void) noexcept; It is even worse when the first declaration is in a system header file, the second declaration does not cause an error during merging but then we lose the throw/noexcept but have the line number of the second decl so when we go and merge in the third decl, we do the error message that way.