Почему структурированные привязки определяются в терминах переменной с уникальным именем?

Почему структурированные привязки определяются через переменную с уникальным именем и все расплывчатое «имя привязано к» языку?

Я лично думал, что структурированные привязки работают следующим образом. Учитывая структуру:

struct Bla
{
    int i;
    short& s;
    double* d;
} bla;

Следующее:

cv-auto ref-operator [a, b, c] = bla;

(примерно) эквивалентно

cv-auto ref-operator a = bla.i;
cv-auto ref-operator b = bla.s;
cv-auto ref-operator c = bla.d;

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

Итак, я явно что-то упускаю, но каков конкретный случай, когда четко определенное расширение в смысле, скажем, выражений сгиба, которые намного проще читать на стандартном языке?

Кажется, что все остальное поведение переменных, определенных структурированной привязкой, на самом деле следует как бы простому «правилу» расширения, которое, как я думаю, будет использоваться для определения концепции.


person rubenvb    schedule 12.04.2018    source источник
comment
bla потенциально сложное выражение с побочными эффектами. Его не следует выполнять несколько раз, если он записан только один раз в исходном коде.   -  person Quentin    schedule 12.04.2018
comment
и есть весь этот расплывчатый специальный язык, используемый для описания того, что должно произойти, если вы спросите меня, что относится ко всему стандарту С++;)   -  person 463035818_is_not_a_number    schedule 12.04.2018
comment
@Квентин, это должен быть ответ   -  person Caleth    schedule 12.04.2018
comment
@Caleth Боюсь, что расплывчатый специальный язык охватывает больше, чем это. Вопрос выиграет от цитаты абзаца, потому что у меня это не выходит из головы;)   -  person Quentin    schedule 12.04.2018


Ответы (2)


Кажется, что все остальное поведение переменных, определенных структурированной привязкой, на самом деле следует как бы простому «правилу» расширения, которое, как я думаю, будет использоваться для определения концепции.

Так оно и есть. За исключением того, что расширение основано не на выражении в правой части, а на введенной переменной. На самом деле это очень важно:

X foo() {
    /* a lot of really expensive work here */
   return {a, b, c};
}

auto&& [a, b, c] = foo();

Если это расширится до:

// note, this isn't actually auto&&, but for the purposes of this example, let's simplify
auto&& a = foo().a;
auto&& b = foo().b;
auto&& c = foo().c;

Это было бы не только крайне неэффективно, но и во многих случаях могло бы быть ошибочным. Например, представьте, если бы foo() был реализован как:

X foo() {
    X x;
    std::cin >> x.a >> x.b >> x.c;
    return x;
}

Поэтому вместо этого он расширяется до:

auto&& e = foo();
auto&& a = e.a;
auto&& b = e.b;
auto&& c = e.c;

это действительно единственный способ гарантировать, что все наши привязки исходят из одного и того же объекта без каких-либо дополнительных затрат.

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

Есть три случая:

  1. Массивы. Каждая привязка действует так, как будто это доступ к соответствующему индексу.
  2. Кортежный. Каждая привязка исходит из вызова std::get<I>.
  3. Совокупный. Каждая привязка называет член.

Это не так уж плохо? Гипотетически #1 и #2 можно было бы объединить (можно добавить механизм кортежа к необработанным массивам), но тогда потенциально более эффективно не делать это.

Значительная часть сложности формулировки (IMO) возникает из-за работы с категориями ценности. Но вам это понадобится независимо от того, как указано что-либо еще.

person Barry    schedule 12.04.2018

Структурированная привязка существует, чтобы разрешить несколько возвращаемых значений в языке, который не позволяет функции разрешаться более чем в одно значение (и, таким образом, не нарушает C++ ABI). Это означает, что какой бы синтаксис ни использовался, компилятор в конечном итоге должен сохранить фактическое возвращаемое значение. И, следовательно, этому синтаксису нужен способ рассказать о том, как вы собираетесь хранить это значение. Поскольку C++ обладает некоторой гибкостью в том, как вещи хранятся (в виде ссылок или значений), синтаксис структурированной привязки должен обеспечивать такую ​​же гибкость.

Следовательно, выбор auto &, auto&& или auto применяется к основному значению, а не к подобъектам.

Во-вторых, мы не хотим влиять на производительность с помощью этой функции. Это означает, что введенные имена никогда не будут копиями подобъектов основного объекта. Они должны быть либо ссылками, либо самими фактическими подобъектами. Таким образом, люди не беспокоятся о влиянии структурированного связывания на производительность; это чистый синтаксический сахар.

В-третьих, система предназначена для обработки как определяемых пользователем объектов, так и массивов/структур со всеми общедоступными членами. В случае определяемых пользователем объектов "имя связано" с подлинной языковой ссылкой, результатом вызова get<I>(value). Если вы сохраните const auto& для объекта, то value будет const& для этого объекта, а get, скорее всего, вернет const&.

Для массивов/общедоступных структур "имена связаны" с чем-то, что не является ссылкой. Они обрабатываются точно так же, как вы вводите value[2] или value.member_name. Выполнение decltype для таких имен не вернет ссылку, если сам распакованный элемент не является ссылкой.

Делая это таким образом, структурированное связывание остается чистым синтаксическим сахаром: оно обращается к объекту любым наиболее эффективным способом, возможным для этого объекта. Для определяемых пользователем типов это вызывает get ровно один раз для каждого подобъекта и сохраняет ссылки на результаты. Для других типов используется имя, которое действует как селектор массива/члена.

person Nicol Bolas    schedule 12.04.2018