Добрый день, дорогой читатель!


Эта статья — первая в цикле статей-конспектов, которые я буду писать по ходу прочтения книги Скотта Мейерса "Эффективный и современный c++". Каждой из таких статей будет соответствовать отдельная директория в специально заведенном на github.com проекте с примерами использования описываемых возможностей и приемов.


Вывод типа шаблона


Удачное проектирование — это когда потребитель не знает как работает устройство, но его всё устраивает.


Вывод типа шаблона — это когда есть шаблонная функция, к примеру, а вызов её осуществляется без всяких таким template и угловых скобочек. Компилятор при этом угадывает какой тип надо будет использовать в конкретном объекте функции.


Мейерс разделяет тип — "T", который выводится и вид — "ParamType", который прописывается программистом в определении параметра.


1. Вид параметра — ссылка или указатель


Для того чтобы было быстро понять правило выведения я буду отражать его в виде шаблона:


входной тип —> тип, параметр —> выведенный тип [, конечный-тип параметра]


Пример со ссылкой:


template<typename T> // выведенный тип T
void func(T& param) // тип параметра - T&

int x = 0; // входной тип int 
func(x);

При выводе типа T ссылочность (*, &) отбрасывается, потому что она уже указана при определении функции, как-бы подразумевается "вы там передавайте что угодно, мы будем считать, что это не ссылка, потому что ссылочность уже добавлена в месте потребления — в нашей функции f"


По такому же принципу отбрасывается константность, если она указана в параметре функции. На ссылки, указатели константность как бы накладывается исключающая маска.


Схема выведения


// отбрасывается ссылочность
int —> (T & param) —> int;
const int —> (T & param) —> const int;
const int & —> (T & param)  —> const int

// отбрасывается константность
int —> (const T & param) —> int;
const int —> (const T & param) —> int;
const int & —> (const T & param) —> int;

// отбрасывается ссылочность  
int * —> (T * param) —> int;
const int —> (T * param) —> const int;
const int * —> (T * param)  —> const int;

// отбрасывается константность
int * —> (const T * param) —> int;
const int —> (const T * param) —> int;
const int * —> (const T * param)  —> int;

2. Тип аргумента — универсальная ссылка


Скотт упоминает, что универсальные ссылки будут рассматриваться позже, поэтому правила для параметров, которые здесь он делит на rvalue и lvalue нужно просто запомнить особо не вдумываясь.


template<typename T>
void func(const T&& param)

Правила вывода для lvalue


Это, утверждает Скотт, единственный случай, когда T выводится как ссылка.
В следующих примерах вид параметра в описании функции — это универсальная ссылка lvalue. Видно, что T выводится как ссылка или константная ссылка, в зависимости
от входного аргумента, тип самого параметра param в этом случае тоже ссылка.


lvalue int —> (T &&param) —> int &, param - int&
lvalue const int —> (T &&param) —> const int &, param - const int&
lvalue const int & —> (T &&param) —> const int &, param - const int&

Тип самого параметра тоже подменяется. Я думаю это связано с тем, что язык не может считать lvalue-перменную, аллоцированную где-то выше тем, что можно дальше перемещать. Разберемся позже, когда дойдем до универсальных ссылок.
Для проверки того, что T — это действтельно тип-ссылка, я написал T myVar в теле шаблонной функции, которая этот T и вывела и ожидаемо получил сообщение — "declared as reference but not initialized" — ссылка же :)


Правила вывода для rvalue


Применяются "обычные" правила из пункта 1


rvalue int —> (T &&param) —> int, param - int&&

Уже сейчас видно, что универсальные ссылки "включаются" только для rvalue-временных объектов, в других случаях, когда аргумент не является универсальной ссылкой, различие rvalue, lvalue не делается


3. Передача по значению (как есть)


При передаче по значению константность и ссылочность исходного аргумента отбрасывается за ненадобностью ведь это абсолютно новые самостоятельные объекты, зачем им квалификаторы того, с чем они не связаны?


int —> (T param) —> int
const int —> (T param) —> int
const int & —> (T param) —> int
// Хитрый пример с отбрасыванием крайней константности указателя, который копируется при передаче
const char * const —> (T param) —>  const char *

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

Комментарии (4)


  1. konshyn
    15.06.2018 21:36
    +6

    Конспект ужасен. Информации меньше, структурирована хуже. Псевдокод страшный для понимания.

    Ладно, если бы статья по мотивам отличной книги что-то дополняла или хотя бы добавлены были интересные примеры, а текст хоть полностью скопирован из книги. Если бы я прочел эту статью до того, как прочел книгу, я бы бросил учить С++ сразу.


    1. rozhkovdmitrii Автор
      16.06.2018 12:05

      Спасибо за комментарий!

      Жаль, что Вам не понравилось :)

      Информации меньше, структурирована хуже.

      Информации действительно меньше, я буду последовательно раскрывать тему в следующих статьях.
      Что касается структуры, возможно Вы нашли бы возможность и время, чтобы выслать мне предметные замечания или изменить оригинал статьи на github и сделать pull request. Цель проста — сделать хороший контент, я был бы Вам благодарен за помощь в этом деле.
      Псевдокод страшный для понимания.

      Все люди разные кому то примеры подавай, кому-то вот такой псевдокод. С учетом того, что правила выведения плоховато лезут в голову — такой вот псевдокод, думаю, при отсутствии альтернатив у Скотта и по настоящему конструктивной критики от Вас — это всё что сейчас есть.
      хотя бы добавлены были интересные примеры, а текст хоть полностью скопирован из книги.

      Есть примеры на github, ссылку я давал в начале статьи. Там я хотел создать некий тренажер, который последовательно выводил бы входной тип и параметр со спецификаторами, а пользователь должен угадать какой тип получится, но столкнулся с рядом сложностей, к которым я не был готов и обошелся только выводом подсказок, подтвержденных реально работающим кодом.

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


      1. dev96
        16.06.2018 18:22

        можете заняться разжёвыванием C++ Core Guidelines) Со своей философией, подробными примерами и моралью.


  1. yrHeTaTeJlb
    16.06.2018 11:19
    +1

    Как говориться, "знание одного правила освобождает от знания многих следствий". Вот универсальная ссылка это следствие. А правило в этом случае выглядит так:
    & + && = &
    & + & = &
    && + & = &
    && + && = &&


    И называется оно "Сворачивание ссылок" https://stackoverflow.com/questions/13725747/concise-explanation-of-reference-collapsing-rules-requested-1-a-a-2