Почему структурированные привязки зависят от tuple_element? - PullRequest
0 голосов
/ 27 апреля 2018

Самый последний проект *1002* предложения структурированных привязок (на котором была основана функция C ++ 17) требует std::tuple_size, член get или std::get и std::tuple_element. Предыдущие черновики требуют только std::tuple_size и члена get или std::get. Насколько я могу судить, не было никакого обсуждения по добавлению этого, это просто появилось в окончательном проекте. Есть ли веская причина требовать специализацию tuple_element, учитывая, что я считаю, что она может быть реализована в целом как

template<std::size_t index, typename T>
struct tuple_element {
    using type = decltype(std::get<index>(std::declval<T>()));
};

Кто-нибудь знает, почему было добавлено это требование?

1 Ответ

0 голосов
/ 27 апреля 2018

Рассмотрим случай:

std::tuple<int, int&>& foo();
auto& [x, y] = foo();

Что такое decltype(x) и что такое decltype(y)? Цель языковой функции заключается в том, чтобы x было просто другим именем для foo().__0, а y - другим именем для foo().__1, что означает, что они должны быть int и int& соответственно. Как указано сегодня, это распаковывается в & dagger; :

auto& __e = foo();
std::tuple_element_t<0, decltype(__e)>& x = std::get<0>(__e);
std::tuple_element_t<1, decltype(__e)>& y = std::get<1>(__e);

А правила работают так, что decltype(x) - это тип, к которому x относится , то есть int. И decltype(y) - это тип, к которому y относится , поэтому int&.

Если мы избежали tuple_element, сделав что-то вроде:

auto&& x = std::get<0>(__e);
auto&& y = std::get<1>(__e);

Тогда мы не могли бы провести различие между x и y, потому что нет никакого способа разграничить то, что делают std::get<0>(__e) и std::get<1>(__e): оба возвращают int&.

Это также способ добавить согласованность между приведенным выше регистром и нормальным структурным регистром:

struct C {
    int i;
    int& r;
};
C& bar();
auto& [a, b] = bar();

Мы хотим, чтобы в целях структурированных привязок a и b здесь вели себя так же, как x и y здесь. И a и b здесь не введенные переменные, это просто разные имена для __e.i и __e.r.

<ч />

В нереферентном случае существует другой сценарий, в котором мы не можем различить:

std::tuple<int, int&&> foo();
auto [x, y] = foo();

Здесь мы сейчас распаковываем через:

auto __e = foo();
std::tuple_element_t<0, decltype(e)>& x = std::get<0>(std::move(__e));
std::tuple_element_t<1, decltype(e)>& y = std::get<1>(std::move(__e));

Оба std::get вызова возвращают int&&, поэтому вы не можете различить их, используя auto&& ... но результаты tuple_element_t различны - int и int&&, соответственно. Эту разницу можно было бы увидеть и в обычном случае структуры.

<ч />

& dagger; Обратите внимание, что из-за CWG 2313 фактически распаковка происходит в ссылку на переменную с уникальным именем, а идентификаторы, указанные в привязке, просто ссылаются на эти объекты.

...