Синтаксис для универсальных ссылок

Это ссылка на значение:

void foo(int&& a);

Это не связывает с lvalues:

int i = 42;
foo(i);   // error

Это универсальная ссылка:

template<typename T>
void bar(T&& b);

Он связывается с rvalues, а также с lvalues:

bar(i);   // okay

Это ссылка на значение:

template<typename T>
struct X
{
void baz(T&& c);
};

Это не связывает с lvalues:

X<int> x;
x.baz(i);   // error

Почему универсальные ссылки используют тот же синтаксис, что и ссылки rvalue? Разве это не ненужный источник путаницы? Рассматривал ли комитет когда-нибудь альтернативные синтаксисы, такие как T&&&, T&*, T@ или же T&42 (шучу над последним)? Если да, то каковы причины отказа от альтернативных синтаксисов?

21

Решение

Универсальная ссылка, такая как T&& может вывести T быть «тип объекта«или»тип ссылки«

В вашем примере это может вывести T как int когда передано значение, поэтому параметр функции int&&или это может вывести T как int& когда передано lvalue, в этом случае параметр функции int& (потому что ссылки сворачиваются правила говорят std::add_rvalue_reference<int&>::type просто int&)

Если T не выводится вызовом функции (как в вашем X::baz пример) тогда это не может быть выведено int&Таким образом, ссылка не является универсальной ссылкой.

Так что IMHO действительно нет необходимости в новом синтаксисе, он хорошо вписывается в правила вывода шаблонов и свертывания ссылок, с небольшой настройкой, что параметр шаблона может быть выведен как ссылочный тип (где в C ++ 03 параметр шаблона функции типа T или же T& всегда выводит T как тип объекта.)

Эта семантика и этот синтаксис были предложены с самого начала, когда в качестве решения проблемы пересылки были предложены ссылки на значения и настройка правил вывода аргументов, см. N1385. Использование этого синтаксиса для обеспечения идеальной пересылки было предложено параллельно с предложением ссылок на rvalue для целей семантики перемещения: N1377 был в той же рассылке, что и N1385. Я не думаю, что альтернативный синтаксис когда-либо был серьезно предложен.

ИМХО альтернативный синтаксис на самом деле был бы более запутанным в любом случае. Если у тебя есть template<typename T> void bar(T&@) как синтаксис для универсальной ссылки, но такой же семантики, как у нас сегодня, то при вызове bar(i) параметр шаблона T можно вывести как int& или же int и параметр функции будет иметь тип int& или же int&& … ни чего не значит «T&@«(каким бы ни был этот тип.) Таким образом, у вас будет грамматика на языке для декларатора T&@ это не тот тип, который может когда-либо существовать, потому что он на самом деле всегда ссылается на какой-то другой тип, либо int& или же int&&,

По крайней мере, с синтаксисом у нас есть тип T&& является реальным типом, и правила свертывания ссылок не являются специфичными для шаблонов функций, использующих универсальные ссылки, они полностью согласуются с остальной частью системы типов вне шаблонов:

struct A {} a;
typedef A& T;
T&& ref = a;    // T&& == A&

Или эквивалентно:

struct A {} a;
typedef A& T;
std::add_rvalue_reference<T>::type ref = a;    // type == A&

когда T является ссылочным типом lvalue, T&& слишком. Я не думаю, что нужен новый синтаксис, правила на самом деле не так уж сложны или запутаны.

21

Другие решения

Почему универсальные ссылки используют тот же синтаксис, что и ссылки rvalue? Разве это не ненужный источник путаницы? Комитет когда-нибудь рассматривал альтернативные синтаксисы …

Да, это сбивает с толку, ИМО (я не согласен с @JonathanWakely здесь). Я помню, что во время неформальной дискуссии (я думаю, за обедом) о раннем дизайне общей функции мы обсуждали разные нотации (Говард Хиннант и Дейв Абрахамс предлагали свою идею, а ребята из EDG давали отзывы о том, как она могла бы соответствовать на базовом языке, это предшествовало N1377). Я думаю, что я помню &? а также &|&& были рассмотрены, но все это было словесным; Я не знаю, были ли сделаны заметки о заседаниях (но я полагаю, что это также, когда Джон предложил использовать && для справки). Однако это были ранние этапы проектирования, и в то время было много фундаментальных семантических проблем, которые нужно было рассмотреть. (Например, во время того же обеденного обсуждения мы также подняли вопрос о возможности не иметь двух видов ссылок, а вместо этого иметь два типа ссылочных параметров.)

Более свежий аспект этой путаницы обнаружен в функции C ++ 17 «Вывод аргументов шаблона класса» (P0099R3). Там подпись шаблона функции формируется путем преобразования подписи конструкторов и шаблонов конструкторов. Для чего-то вроде:

template<typename T> struct S {
S(T&&);
};

подпись шаблона функции

template<typename T> auto S(T&&)->S<T>;

формируется для использования для вычета декларации, как

int i = 42;
S s = i;  // Deduce S<int> or S<int&>?

Выведение T = int& здесь было бы нелогичным. Таким образом, мы должны добавить «специальное правило вычета, чтобы отключить специальное правило вычета» в этом случае 🙁

4

По вопросам рекламы [email protected]