Как использование безымянных пространств имен в заголовках может привести к нарушениям ODR?

В Руководстве по стилю Google C++ в разделе " Пространства имен " говорится, что "использование безымянных пространств имен в заголовочных файлах может легко привести к нарушению правила определения C++ One Definition Rule (ODR)".

Я понимаю, почему не использование безымянных пространств имен в файле реализации может привести к нарушениям ODR, но не то, как это может сделать использование в заголовке. Как это может вызвать нарушение?

2 ответа

Решение

Причина в том, что если вы действительно используете что-либо в анонимном пространстве имен, вы рискуете неопределенным поведением. Например:

namespace {
double const pi = 3.14159;
}

inline double twoPiR( double r ) { return 2.0 * pi * r; }

Правило для встроенных функций (и классов, и шаблонов, и всего остального, что должно быть определено в нескольких единицах перевода) состоит в том, что токены должны быть идентичны (обычно это так, если вы не нажмете какой-то макрос), и что все символы должны связываться одинаково, В этом случае каждая единица перевода имеет отдельный экземпляр pi, Итак pi в twoPiR привязывается к другому объекту в каждой единице перевода. (Есть несколько исключений, но все они содержат интегральные выражения.)

Конечно, даже без анонимного пространства имен это было бы неопределенным поведением здесь (так как const означает внутреннюю связь по умолчанию), но основной принцип сохраняется. Любое использование в заголовке чего-либо в безымянном пространстве имен (или любого объекта const, определенного в заголовке) может вызвать неопределенное поведение. Является ли это реальной проблемой или нет, зависит, но, безусловно, от всего, что действительно связано с piвыше, будет вызывать проблемы. (Я говорю "действительно" здесь, потому что во многих случаях адрес или ссылка формально используются, но на практике встроенное расширение приведет к фактическому использованию значения. И, конечно, токен 3.14159 является 3.14159независимо от того, где он появляется.)

В test.h

namespace {
  int x;
}

namespace{
  int x;
}

Включение этого заголовочного файла в любой исходный файл приведет к нарушению ODR, поскольку x определяется дважды. Это происходит потому, что безымянному пространству имен присваивается уникальный идентификатор компилятором, а всем вхождениям безымянного пространства имен в модуле перевода присваивается один и тот же идентификатор. Перефразируя: каждый TU имеет не более одного безымянного пространства имен.

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