C / C ++ правила замены / переопределения? - PullRequest
1 голос
/ 18 апреля 2009

Я не особенно новичок в C / C ++, но сегодня я обнаружил некоторые вещи, которые я не ожидал. Это компилируется в gcc:


/* test.c */
#include <stddef.h> // !

typedef unsigned long int size_t; // NO ERROR
typedef unsigned long int size_t; // NO ERROR

int
main(void)
{
  typedef unsigned long int size_t; // NO ERROR
  return 0;
}

Это не:


/* test.c */
#include <stddef.h>

typedef unsigned long int size_t; // NO ERROR
typedef unsigned long int size_t; // NO ERROR

int
main(void)
{
  typedef unsigned long int size_t; // NO ERROR
  typedef unsigned long int size_t; // ERROR
  return 0;
}

Это не так:


/* test.<b>h</b> */ // ! header
typedef unsigned long int size_t;
typedef unsigned long int size_t; // ERROR

Аналогично в g ++ это компилируется:


/* test.h */ // ! header
#include <cstddef>

inline void* operator new(size_t, void* p) throw() { return p; }

Это не:


/* test.h */ // ! header
#include <new> // !

inline void* operator new(size_t, void* p) throw() { return p; } // ERROR

Это делает:


/* test.cc */
#define _NEW

#include <new> // !
#include <iostream>
#include <cstdlib>

using std::cout;
using std::endl;

inline void* operator new(size_t size) throw() // NO ERROR EXPECTED
{
  cout << "OPERATOR NEW CALLED" << endl;
  return malloc(size);
}

inline void* operator new(size_t, void* p) throw() // NO ERROR
{
  cout << "PLACEMENT NEW CALLED" << endl;
  return p;
}

int main()
{
  char *buffer[4];
  int *i = new (buffer) int;
  int *j = new int;
  return 0;
}

(Замена стандартного нового оператора работает во всех вышеупомянутых случаях. Замена нового оператора является незаконной, я знаю.)

Шаблон легко увидеть, но кто-нибудь может предложить мне «стандартное» объяснение? Почему я могу делать в файлах .c или .cc то, что не могу в файлах .h (переопределить старые typedefs, заменить функции, которые нельзя заменить)?


Спасибо за ответ. Я пропустил некоторый код, включая защиту заголовков. В последнем примере .cc я HTML закодировал << как & g t; & g t; по ошибке и забыл включить cstdlib. Я исправил код, чтобы он компилировался. Тем не менее, еще одна вещь, которую я пропустил, была #define _NEW, что оказалось решающим. По-видимому, в GNU защита заголовка <new>определяет _NEW, поэтому мое определение здесь предотвратило включение стандартного нового заголовка, поэтому замена работала

m@m:~/Desktop/Library$ ./a.out 
PLACEMENT NEW CALLED
OPERATOR NEW CALLED

Так что, да, единственное, что остается необъяснимым, это то, почему я могу многократно переписывать вещи в файлах .c / .cc, но не в .h, вот так:

/* test.c */
#include <stddef.h>

typedef unsigned long int size_t; // NO ERROR
typedef unsigned long int size_t; // NO ERROR

int
main(void)
{
  typedef unsigned long int size_t; // NO ERROR
  return 0;
}

Не то чтобы я все равно хотел это сделать, но просто интересно.

РЕДАКТИРОВАТЬ: Спасибо, это действительно отвечает на все это. Изменение его на xyz не позволяет использовать несколько определений в данной области, что кажется правильным. :) Причина, по которой я проводил эти маленькие тесты, состоит в том, что я пишу подмножество C и C ++ для небольшой операционной системы, но поскольку я использую существующие библиотеки, не удаляя ничего, чтобы помочь мне в моем тестировании, я пытался понять как убедиться, что (1) мой код вызывается в тестах (например, мое размещение нового) и (2) что мой код не конфликтует с библиотеками GNU. Ответ на (1) теперь кажется довольно очевидным. Я просто # определяю макрос охранника заголовка из стандартного X.h в свой X.h:).

Ответы [ 2 ]

3 голосов
/ 18 апреля 2009

Используя GCC 4.0.1 на MacOS X 10.4.11 (древний - но так же и компьютер), пример с «test.h» работает - или моя адаптация этого делает. Похоже, вы можете иметь столько (идентичных) глобальных typedefs 'size_t', сколько захотите - у меня было 5 с версией в stddef.h.

Первый typedef внутри main является «очевидно» легальным; это новая область и может определять новое значение для имени.

Обоснование C99 говорит:

В C89, typedef может быть повторно объявлен во внутреннем блоке с объявлением, которое явно содержит имя типа. Это правило позволило избежать двусмысленности относительно того, следует ли использовать typedef в качестве имени типа или кандидата для переопределения. В C99 неявные объявления int недопустимы, поэтому эта двусмысленность [sic!] Невозможна, и правило больше не нужно.

Позже, обсуждая стандартные заголовки, также говорится:

Комитет С89 решил сделать библиотечные заголовки «идемпотентными», то есть они должны быть включаемое любое количество раз и включаемое в любом порядке. Это требование, которое отражает широко распространенная существующая практика, может потребовать некоторых защитных оболочек в заголовках, чтобы избегайте, например, переопределения typedefs. Чтобы гарантировать, что такая защитная упаковка может быть для правильной работы с typedefs стандартные заголовки могут быть включены только вне любого объявления.

Следовательно, очевидно, что переопределения typedef в одной области (например, области файла) вообще не допускаются. Поэтому я думаю, что множественные внешние переопределения size_t, вероятно, являются либо особенностью GCC, либо ошибкой, собственно, функцией.

Если вы измените size_t на xyz, вы получите гораздо больше ошибок.
2 голосов
/ 18 апреля 2009

Я не уверен, как несколько typedef скомпилируются в вашем случае. Помните, что заголовки компилируются не сами по себе, а в сочетании с классом реализации. Кроме того, в ваших заголовках нет защиты заголовков или директивы #pragma once. Какие варианты вы используете?

Что касается размещения new - оно явно запрещено стандартом (18.5.1.3) - так что не будет работать.

Здесь нет шаблона - за исключением переопределения уже объявленных символов.

Кстати: ни один из ваших .c или .cpp примеров не скомпилирован с Comeau .

...