TGViewer
Грокаем C++ Грокаем C++ @grokaemcpp · 9.35K subscribers
Post #371 2.52K
​​ParamType - универсальная ссылка
#опытным

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

Только при такой сигнатуре шаблонной функции можно считать ее параметр универсальной ссылкой:

template <class T>
void func(T&& param) {...}

func(expression);


То есть это rvalue reference на cv-неквалифицированный тип. Только в таком виде тип param называется универсальной ссылкой. Как говорят в школе:
И ни в каком другом виде!

Ни
template <class T>
void func(std::vector<T>&& param) {...}

Это просто rvalue reference.
Ни
template <class T>
void func(const T&& param) {...}
Это тоже просто rvalue reference! Только константный.
И к последним двум кейсам применяются правила
отсюда
 и 
отсюда.


Когда expression - rvalue reference, то Т выводится безссылочным типом, чтобы тип ParamType был rvalue reference of T. Если тип expression - lvalue, то Т выводится в тип lvalue reference. Самое интересное, что это единственный кейс, когда тип Т выводится в ссылку.

Есть такое правило, что & + && = &. То есть при использовании универсальной ссылки в параметре шаблонной функции при передаче туда lvalue|lvalue reference, этот параметр выводится в lvalue reference. Это происходит именно за счет того, что шаблонный тип выводится в тип lvalue reference. Условно: функция принимает Т && , T выводится в int&, подставляем Т в параметр функции и получаем int& &&. Но такого синтаксиса нет и 2 ссылки коллапсируют в одну левую ссылку int&.


template<typename T> void f(T&& param); // param is a universal reference

int x = 27;
const int cx = x;
const int& lrx = x;
int&& rrx = 42;

f(x); // x is lvalue, so T is int&, param's type is also int&
f(cx); // cx is lvalue, so T is const int&, param's type is also const int&
f(lrx); // lrx is lvalue, so T is const int&, param's type is also const int&
f(27); // 27 is prvalue, so T is int, param's type is therefore int&&
f(std::move(rrx)); // rrx is xvalue, so T is int, param's type is therefore int&&


Обратите внимание на первые 3 кейса. Там Т выводится в lvalue reference тип. В двух последних Т - просто int безо всяких ссылок.

Мы на самом деле уже обсуждали универсальные ссылки в рамках серии статей про категории выражения. Вот ссылочка на эту статью с более полным описанием процессов.

В этой статье я просто хотел подсветить самые важные моменты в этой теме, которые касаются именно вывода типов.

Stay universal. Stay cool.

#cppcore #cpp11 #template
  • 👍 10
  • 🔥 8
  • ❤ 2
More from @grokaemcpp
  1. Oct 14, 2026​​Что внутри std::condition_variable? #опытным Более менее все знакомы с примитивом синхро…
  2. Oct 12, 2026​​WAT #новичкам Как вы думаете, результат выражения: bool math = -1 < 1U; будет правдой ил…
  3. Oct 8, 2026​​Strict weak ordering #опытным На первый взгляд, всё выглядит рабочим: мы создаём 40 зака…
  4. Oct 7, 2026​​Где-то баг... #опытным Вот вам код: struct Order { int price; int id; }; int main() { st…
  5. Oct 5, 2026Откуда spurious wakeup на кондваре? #опытным У кондваров есть метод std::condition_variabl…
  6. Oct 1, 2026​​Stacktrace. Tips #опытным Чтобы полноценно работать со стандартными трейсами, нужно знат…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →