Почему оператор C++ new/delete/ варианты не должен находиться в заголовочных файлах?

Может кто-нибудь объяснить природу этой ошибки компиляции C++? Я балуюсь / учусь о перегрузке глобальных операторов new, delete и их вариантов. Я прочитал пару статей на эту тему, но я не смог найти ту, которая, по-видимому, касается именно этого.

Код

foo.h:

#ifndef foo_h
#define foo_h

void* operator new(size_t);
void* operator new[](size_t);

void operator delete(void*);
void operator delete[](void*);

#endif // foo_h

foo.cpp:

#include <foo.h>
#include <iostream>

void* operator new(size_t size) { return NULL; }
void* operator new[](size_t size) { return NULL; }

void operator delete(void* p) { }
void operator delete[](void* p) { }

Ошибка компиляции

>g++ -g -std=c++14 -I./ -c foo.cpp -o foo.o
In file included from /usr/lib/gcc/x86_64-pc-cygwin/5.4.0/include/c++/ext/new_allocator.h:33:0,
                 from /usr/lib/gcc/x86_64-pc-cygwin/5.4.0/include/c++/x86_64-pc-cygwin/bits/c++allocator.h:33,
                 from /usr/lib/gcc/x86_64-pc-cygwin/5.4.0/include/c++/bits/allocator.h:46,
                 from /usr/lib/gcc/x86_64-pc-cygwin/5.4.0/include/c++/string:41,
                 from /usr/lib/gcc/x86_64-pc-cygwin/5.4.0/include/c++/bits/locale_classes.h:40,
                 from /usr/lib/gcc/x86_64-pc-cygwin/5.4.0/include/c++/bits/ios_base.h:41,
                 from /usr/lib/gcc/x86_64-pc-cygwin/5.4.0/include/c++/ios:42,
                 from /usr/lib/gcc/x86_64-pc-cygwin/5.4.0/include/c++/ostream:38,
                 from /usr/lib/gcc/x86_64-pc-cygwin/5.4.0/include/c++/iostream:39,
                 from foo.cpp:2:
/usr/lib/gcc/x86_64-pc-cygwin/5.4.0/include/c++/new:116:41: error: declaration of ‘void operator delete(void*) noexcept’ has a different exception specifier
   __attribute__((__externally_visible__));
                                         ^
In file included from foo.cpp:1:0:
./foo.h:8:6: error: from previous declaration ‘void operator delete(void*)’
 void operator delete(void* p);
      ^
In file included from /usr/lib/gcc/x86_64-pc-cygwin/5.4.0/include/c++/ext/new_allocator.h:33:0,
                 from /usr/lib/gcc/x86_64-pc-cygwin/5.4.0/include/c++/x86_64-pc-cygwin/bits/c++allocator.h:33,
                 from /usr/lib/gcc/x86_64-pc-cygwin/5.4.0/include/c++/bits/allocator.h:46,
                 from /usr/lib/gcc/x86_64-pc-cygwin/5.4.0/include/c++/string:41,
                 from /usr/lib/gcc/x86_64-pc-cygwin/5.4.0/include/c++/bits/locale_classes.h:40,
                 from /usr/lib/gcc/x86_64-pc-cygwin/5.4.0/include/c++/bits/ios_base.h:41,
                 from /usr/lib/gcc/x86_64-pc-cygwin/5.4.0/include/c++/ios:42,
                 from /usr/lib/gcc/x86_64-pc-cygwin/5.4.0/include/c++/ostream:38,
                 from /usr/lib/gcc/x86_64-pc-cygwin/5.4.0/include/c++/iostream:39,
                 from foo.cpp:2:
/usr/lib/gcc/x86_64-pc-cygwin/5.4.0/include/c++/new:118:41: error: declaration of ‘void operator delete [](void*) noexcept’ has a different exception specifier
   __attribute__((__externally_visible__));
                                         ^
In file included from foo.cpp:1:0:
./foo.h:9:6: error: from previous declaration ‘void operator delete [](void*)’
 void operator delete[](void* p);
      ^

Некоторые странности по этому вопросу, которые я считаю актуальными:

  • Если я закомментирую #include <iostream> в foo.cpp, компиляция удалась
  • Если я закомментирую объявления функций в foo.h и только сохранить их определения, в foo.cpp (а также сохраняя #include <iostream>), компиляция удалась.

У меня есть смутные подозрения; возможно, ответчики подтвердят в своих ответах:

  • Ошибка упоминает о exception specifier поэтому я подумал, что, возможно, переопределив любой из этих операторов, я должен переопределить весь набор их братьев и сестер. Тем не менее, добавив operator delete(void*, const std::nothrow_t&) объявление и определение не изменили ошибку компиляции. Я также не думаю, что это должно быть правдой, что переопределение любого из этих операторов обязывает кодировщика реализовать их все, но я ошибаюсь в этом?
  • Я прочитал статью за пределами Stackru, в которой говорится, что эти операторы должны быть включены только в одну "единицу перевода" и, следовательно, не должны быть в заголовочных файлах. Я не понимаю, что такое переводческая единица, и эта статья не объясняет, что это такое. Если это связано с этой проблемой, пожалуйста, объясните, что такое "единица перевода" и почему это требует исключения объявлений функций из файла заголовка - это кажется противоречащим всем моим предыдущим опытом программирования на C++.

Спасибо за понимание.

1 ответ

Решение

Проблема, которую вы видите, связана с различиями в следующих декларациях.

Библиотека объявляет operator delete функционирует как:

void operator delete(void*) noexcept;
void operator delete [](void*) noexcept;

пока вы объявляете их как:

void operator delete(void*);
void operator delete [](void*);

Вместо того, чтобы объявлять их в вашем.h файле, вы должны использовать

#include <new>

Раздел поиска 18.6. Динамическое управление памятью стандарта C++11, если у вас есть доступ к нему для получения дополнительной информации по этому вопросу.

Единицей перевода обычно является файл.cpp. Дальнейшее чтение: Что такое "единица перевода" в C++.

Другие вопросы по тегам