Ответ 1
Да. Структурированные привязки и ссылки экспедиционных хорошо перемешайте †.
В общем, в любом месте ‡ вы можете использовать auto
, вы можете использовать auto&&
для получения различного значения. В частности, для структурированных привязок это происходит из [dcl.struct.bind]:
В противном случае
e
определяется как-еслиattribute-specifier-seq opt decl-specifier-seq ref-qualifier opt
e
initializer;
где объявление никогда не интерпретируется как объявление функции, а части декларации, отличные от идентификатора declarator, берутся из соответствующей декларации структурированного связывания.
Дополнительные ограничения на эти разделы в [dcl.dcl]:
Простая декларация с идентификатором-списком называется объявлением структурированной привязки ([dcl.struct.bind]). Спецификатор decl-specifier-seq должен содержать только
auto
и-квалификаторы типа-спецификатора. Инициализатор должен иметь вид "=
присваивания-выражение", вид "{
присвоение выражение}
", либо в форме "(
присваивания-выражение)
", где присваивание выражение-массив или не накидной класс тип.
Объединяя это, мы можем разбить ваш пример:
auto&& [bla, blabla] = something();
как объявление этой неназванной переменной:
auto && e = something();
~~~~ ~~ ~~~~~~~~~~~
decl-specifier-seq initializer
ref-qualifier
Поведение - это результат, полученный из [dcl.spec.auto] (особенно здесь). Там мы делаем вывод против инициализатора:
template <typename U> void f(U&& );
f(something());
где auto
было заменено на U
, а &&
переносится. Здесь наш справочник. Если дедукция терпит неудачу (что могло бы быть только в случае, если something()
было void
), наша декларация плохо сформирована. Если это удастся, мы возьмем выведенное U
и рассмотрим наше выражение так, как если бы оно было:
U&& e = something();
Который делает e
ссылкой lvalue или rvalue, то есть const, не соответствующей, на основе категории стоимости и типа something()
.
Остальные правила структурированных привязок следуют в [dcl.struct.bind] на основе базового типа e
, независимо от того, является ли something()
значение lvalue, и является ли e
ссылкой на lvalue.
† С одной оговоркой. Для структурированного связывания decltype(e)
всегда является ссылочным типом, а не типом, который вы могли бы ожидать. Например:
template <typename F, typename Tuple>
void apply1(F&& f, Tuple&& tuple) {
auto&& [a] = std::forward<Tuple>(tuple);
std::forward<F>(f)(std::forward<decltype(a)>(a));
}
void foo(int&&);
std::tuple<int> t(42);
apply1(foo, t); // this works!
Я передаю свой tuple
- это значение lvalue, которое вы ожидаете передать своим базовым элементам в качестве ссылок lvalue, но они фактически отправляются. Это связано с тем, что decltype(a)
- это просто int
(ссылочный тип), а не int&
(осмысленный способ поведения a
). Что-то нужно иметь в виду.
‡ Есть два места, где я могу думать, где это не так.
В объявлениях типа trailing-return-type вы должны использовать только auto
. Вы не можете писать, например:
auto&& foo() -> decltype(...);
Единственное другое место, где я могу думать о том, где это может быть не так, - это часть Concepts TS, где вы можете использовать auto
в других местах для вывода/ограничения типов. Там, используя ссылку пересылки, когда тип, который вы выводите, не является ссылочным, был бы плохо сформирован. Я думаю:
std::vector<int> foo();
std::vector<auto> a = foo(); // ok, a is a vector<int>
std::vector<auto&&> b = foo(); // error, int doesn't match auto&&