Ответ 1
Цель использования odr
Неформально использование odr переменной означает следующее:
Если какое-либо выражение в любом месте программы принимает адрес ссылки или привязывает ссылку непосредственно к объекту, этот объект должен быть определен.
Разъяснение в последнем проекте
В последней версии спецификации §3.2 было выяснено (см. Проект С++ 14 на GitHub):
2 Выражение потенциально оценивается, если оно не является неоцененным операндом (п. 5) или его подвыражением. Набор потенциальных результатов выражения
e
определяется следующим образом:
- Если
e
является id-выражением (5.1.1), набор содержит толькоe
.- Если
e
- выражение доступа к члену класса (5.2.5), набор содержит потенциальные результаты выражения объекта.- Если
e
- это выражение-указатель-член (5.5), второй операнд которого является константным выражением, набор содержит потенциальные результаты выражения объекта.- Если
e
имеет вид (e1), то множество содержит потенциальные результаты e1.- Если
e
- условное выражение glvalue (5.16), то множество представляет собой объединение множеств потенциальных результатов второго и третьего операндов.- Если
e
- это запятое выражение (5.18), то множество содержит потенциальные результаты правого операнда.- В противном случае набор пуст.
[Примечание. Этот набор является (возможно, пустым) набором id-выражений, каждый из которых либо
e
, либо подвыражениеe
.[Пример: В следующем примере набор потенциальных результатов инициализатора
n
содержит первое подвыражениеS::x
, но не второе подвыражениеS::x
.struct S { static const int x = 0; }; const int &f(const int &r); int n = b ? (1, S::x) // S::x is not odr-used here : f(S::x); // S::x is odr-used here, so // a definition is required
-end example] -end note]
3 Переменная
x
, имя которой отображается как потенциально оцененное выражениеex
, используется , если не применить преобразование lvalue-to-rval (4.1) вx
дает постоянное выражение (5.19), которое не вызывает никаких нетривиальных функций и, еслиx
является объектом,ex
является элементом множества потенциальных результатов выраженияe
, где либо lvalue- to-rvalue (4.1) применяется кe
, илиe
является выражением отбрасываемого значения (раздел 5).
Какова была ситуация в С++ 11?
§3.2/2 в С++ 11 гласит:
Выражение потенциально оценивается, если оно не является неоцененным операндом (п. 5) или его подвыражением. Переменная, имя которой отображается как потенциально оцениваемое выражение, используется odr, если это не объект, который удовлетворяет требованиям для отображения в постоянном выражении (5.19), а преобразование lvalue-to-rvalue (4.1) - немедленно.
Проблема с этими формулировками была DR 712. Рассмотрим этот пример:
struct S {
static const int a = 1;
static const int b = 2;
};
int f(bool x) {
return x ? S::a : S::b;
}
Так как S::a
и S::b
являются lvalues, условное выражение x ? S::a : S::b
также является lvalue. Это означает, что преобразование lvalue-to-rvalue не сразу применяется к S::a
и S::b
, а к результату условного выражения. Это означает, что по формулировке С++ 11 эти статические члены данных используются как odr, и требуется определение. Но на самом деле используются только значения, поэтому не обязательно определять статические члены данных - достаточно было бы объявления. Новая формулировка проекта С++ 14 разрешает это.
Вызывает ли новая формулировка все проблемы?
Нет. В следующем примере переменная S::a
по-прежнему используется odr:
struct S { static constexpr int a[2] = {0, 1}; };
void f() {
auto x = S::a[0];
}
Поэтому я отправил новую проблему, чтобы добавить следующую марку в §3.2/2:
- если
e
является выражением индексирования glvalue (5.2.1) формыE1[E2]
, множество содержит потенциальные результатыE1
.