Подтвердить что ты не робот

Когда переменная odr используется в С++ 14?

Проект С++ 14 (N3936) гласит в § 3.2/3:

Переменная x, имя которой отображается как потенциально вычисленное выражение ex, используется odr, если применение преобразования lvalue-to-rvalue (4.1) к x не дает постоянного выражения (5.19), которое не вызывает никаких нетривиальных функций и, если x является объектом, ex является элементом множества потенциальных результатов выражения e, где либо lvalue-rvalue conversion (4.1) применяется к e, либо e является выражением отбрасываемого значения (пункт 5).

Это не имеет для меня никакого смысла: если выражение e является выражением отбрасываемого значения, оно зависит от контекста, в котором используется e. Каждое выражение, используемое в выражении-выражении (§6.2), является выражением отбрасываемого значения. Если преобразование lvalue-rvalue применяется к e, также зависит от контекста e.

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

Почему это изменилось с С++ 11 на С++ 14? И как это следует интерпретировать? Как бы то ни было, это не имеет смысла.

4b9b3361

Ответ 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.