Можете ли вы иметь два класса с одинаковым именем и одной и той же функцией-членом в разных единицах перевода?

#c #class #linker #linkage

#c #класс #компоновщик #связь

Вопрос:

Предположим, у меня есть две единицы перевода:

 //A.cpp
class X
{
};

//B.cpp
class X
{
   int i;
};
  

Хорошо ли сформирована приведенная выше программа?

Если нет, то больше вопросов нет. Если ответ положительный, программа правильно сформирована (игнорируйте отсутствие main), тогда второй вопрос. Что, если в них есть функция с таким же именем?

 //A.cpp
class X
{
    void f(){}
};

//B.cpp
class X
{
    int i;
    void f(){}
};
  

Будет ли это проблемой для компоновщика, поскольку он будет видеть amp;X:: f в обоих объектных файлах? Являются ли анонимные пространства имен обязательными в такой ситуации?

Ответ №1:

Хорошо ли сформирована приведенная выше программа?

Нет. Это нарушает Правило одного определения:

[базовый.определение odr]

Может существовать более одного определения a

  • тип класса ([класс]),

в программе при условии, что каждое определение отображается в разных единицах перевода и определения удовлетворяют следующим требованиям. Учитывая, что такая сущность D определена более чем в одной единице преобразования, для всех определений D или, если D является безымянным перечислением, для всех определений D, которые достижимы в любой заданной точке программы, должны быть выполнены следующие требования.

  • Каждое такое определение должно состоять из той же последовательности токенов, где указано определение типа замыкания …

Являются ли анонимные пространства имен обязательными в такой ситуации?

Если вам нужны разные определения классов, они должны быть разных типов. Пространство имен с уникальным именем — это один из вариантов, а анонимное пространство имен — гарантированный способ получить уникальное (для единицы перевода) пространство имен.

Ответ №2:

Краткая версия

Ну, нет… C основан на предположении, что каждое имя в пространстве имен уникально. Если вы нарушите это предположение, у вас будет 0 гарантий, что это сработает.

Например, если у вас есть методы с одинаковым именем в двух единицах перевода ( *.o файлах). Компоновщик не будет знать, какой из них использовать для данного вызова, поэтому он просто вернет ошибку.

Длинная версия

… но на самом деле да!

На самом деле существует довольно много ситуаций, когда вам могло бы сойти с рук использование классов / методов с тем же именем.

На самом деле не используйте ни один из этих приемов в своих программах! Компиляторы вольны делать практически все, что угодно, если они думают, что это оптимизирует результирующую программу, так что любое из приведенных ниже предположений может нарушиться.

Классы самые простые. Давайте возьмем какой-нибудь класс только с нестатическими членами и без функций. Такая вещь даже не оставляет следов в скомпилированной программе. Классы / структуры — это всего лишь инструменты для программиста по организации данных, поэтому нет необходимости иметь дело с пулами памяти и смещениями. Итак, в принципе, если у вас есть два класса с одинаковым именем в разных единицах компиляции, это должно сработать. После того, как компилятор закончит с ними, они будут состоять всего из нескольких инструкций о том, на сколько переместить указатель в памяти, чтобы получить доступ к определенному полю. Здесь вряд ли есть что-то, что могло бы сбить компоновщика с толку.

Функции и переменные (включая статические переменные класса) сложнее, потому что компилятор часто создает для них символы в *.o файле. Если вам повезет, компоновщик может игнорировать их, если такая функция / переменная не используется, но я бы даже не рассчитывал на это. Однако есть способы не создавать для них символы. Статические глобальные элементы или элементы в анонимных пространствах имен не видны за пределами их единиц перевода, поэтому компоновщик не должен жаловаться на них. Кроме того, встроенные функции не существуют как отдельные объекты, поэтому у них также нет символов, что здесь особенно актуально, потому что функции, определенные внутри классов, встроены по умолчанию. Если символа нет, компоновщик не увидит конфликта, и все должно скомпилироваться.

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

В заключение, если ваши классы не имеют статических членов и они не определяют функции вне своих тел, может быть возможно иметь два класса с одинаковым именем, если вы не включаете их в один файл. Однако это чрезвычайно хрупко. Даже если это работает прямо сейчас, в новой версии компилятора могут быть некоторые исправления / оптимизации / изменения, которые сломали бы такую программу. Не говоря уже о том факте, что includes, как правило, довольно тесно переплетаются в более крупных проектах, так что есть приличный шанс, что в какой-то момент вам понадобится включить оба файла в одно и то же место.