Одно правило определения в переполнении стека

Согласно стандарту c ++:

Ни одна единица перевода не должна содержать более одного определения любого
переменная, функция, тип класса, тип перечисления или шаблон.

//--translation_unit.cpp--//
int a;

void foo()
{
int a; //Second defention of a. ODR fails.
}

Можете ли вы объяснить мне, как на самом деле работает ODR?

0

Решение

Это не нарушает правила, потому что вы определяете две разные переменные. Они имеют одинаковые имена, но объявляются в разных областях, и поэтому являются отдельными объектами. У каждого есть одно определение.

Объявление в области действия функции называется скрывать один в глобальном пространстве имен. В рамках функции неквалифицированное имя a относится к локальной переменной, в то время как квалифицированное имя ::a относится к глобальному.

11

Другие решения

Вы не определили a снова.

Вы только что определили новую переменную a, Он имеет область видимости только внутри функции и не имеет ничего общего с исходной областью (которая имеет глобальную область видимости) и скрывает исходную область внутри функции.

0

Они не нарушают ODR, потому что имеют различную сферу применения.

Первый a имеет глобальный охват

Известна переменная с глобальной областью действия (также называемая областью файлов)
по всему файлу после точки, где он определен

Второй a имеет локальная сфера

Известна переменная с локальной областью действия (также называемая блочной областью действия)
только в пределах блока, в котором он определен

Для более ясного понимания ODR в C ++ необходимо изучить следующие концепции: Продолжительность хранения, объем и связь

0

Можете ли вы объяснить мне, как на самом деле работает ODR?

Вот пример нарушения ODR:

/* file : module.cpp */
#include <stdio.h>
inline int foo() {
printf("module.foo: %p\n", &foo);
return 1;
}
static int bar = foo();

/* file : main.cpp */
#include <stdio.h>
inline int foo() {
printf("main.foo: %p\n", &foo);
return 2;
}
int main(int argc, char *argv[]) {
return foo();
}

Как видите, функция int foo() определяется по-разному в двух модулях. Теперь посмотрим, как это приводит к разному поведению в зависимости от запрошенного уровня оптимизации (O3 против O0):

$ clang++ --std=c++11 -O0 main.cpp module.cpp && ./a.out
module.foo: 0x100a4aef0
module.foo: 0x100a4aef0

$ clang++ --std=c++11 -O3 main.cpp module.cpp && ./a.out
module.foo: 0x101146ee0
main.foo: 0x101146ee0

Выходные данные различны, поскольку для встроенных функций компилятор создает символ компоновщика в каждом модуле компиляции. Этот символ (в каждом модуле компиляции) помечен как «выберите любой, они все одинаковые». В первом случае, когда все оптимизации отключены, компоновщик выбирает определение из module.cpp. Во втором случае компилятор просто включает обе функции, поэтому никакой дополнительной работы с компоновщиком не требуется.

Есть и другие примеры, когда нарушение ODR вызывает странное поведение. Так что не делай этого 🙂

Постскриптум Бонус, случай из реальной жизни:

/* a.cpp */
struct Rect
{
int x,y,w,h;
Rect(): x(0),y(0),w(0),h(0)
};
/* b.cpp */
struct Rect
{
double x,y,w,h;
Rect(): x(0),y(0),w(0),h(0)
};

Проблема здесь та же, что и в предыдущем примере (поскольку конструкторы Rect неявно встроены). В зависимости от фазы луны компилятор выбирал ту или иную реализацию, получая странные результаты (int версия оставит часть doubles неинициализированным, double версия выйдет за пределы intи испорченная память там). Хороший способ защититься от этого — использовать анонимные пространства имен (C ++) или объявить struct as static (С):

/* file.cpp */
namespace {
struct Rect { ... };
}
/* file.c */
static struct Rect { ... };
0
По вопросам рекламы [email protected]