анонимные пространства имен и правило единого определения

#c #namespaces #one-definition-rule

#c #пространства имен #правило одного определения

Вопрос:

Нарушаю ли я правило единого определения в следующей программе?

 // foo.hpp
#ifndef FOO_HPP_
#define FOO_HPP_

namespace {
   inline int foo() {
       return 1;
   }
}

inline int bar() {
    return foo();
}
#endif
//EOF
 

и

 // m1.cpp

#include "foo.hpp"

int m1() {
    return bar();
}

//EOF
 

и

 // m2.cpp

#include "foo.hpp"

int m2() {
    return bar();
}

//EOF
 

и, наконец,

 // main.cpp
#include <iostream>

int m1();
int m2();

int main(int, const char* [])
{
    int i = m1();
    int j = m2();

    std::cout << (i j) << std::endl;
    return 0;
}

// EOF
 

В приведенном выше обратите внимание, что foo() это определено в анонимном пространстве имен, поэтому я ожидаю, что каждая единица перевода m1.cpp и m2.cpp получит свою собственную версию, поэтому нарушения ODR нет. С другой стороны, bar() это просто обычная старая встроенная функция, которая вызывает 2 разных foo s. Так что это нарушает ODR, верно?

Обновление: ранее у меня были макросы в определении foo , которые изменяли возвращаемое значение и каждое из m1 и m2 определяли макрос по-разному перед включением foo.hpp . (И в этом предыдущем примере g будет создан двоичный файл, который выводится (i j) со значением, отличным от ожидаемого.) Но на самом деле эта программа нарушает ODR, даже если тело foo() идентично.

Комментарии:

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

Ответ №1:

Это нарушает ODR. См. 3.2 / 5, в котором говорится о внешних встроенных функциях ( bar ):

в каждом определении D соответствующие имена, просмотренные в соответствии с 3.4, должны ссылаться на объект, определенный в определении D, или должны ссылаться на тот же объект…

В этом случае bar ссылается на две разные версии foo , тем самым нарушая правило.

Ответ №2:

Да, это определение bar() действительно нарушает правило единого определения. Вы создаете несколько определений, каждое из которых вызывает другую вызываемую функцию foo() .

Как вы говорите, это было бы нарушением, даже если бы все версии foo() были идентичны, поскольку они все равно являются разными функциями.