11 июля 2020

#14 Доступность идентификатора при его инициализации

При объявлении и определении переменной можно использовать её идентификатор в правой части. Для обычных переменных это имеет мало смысла:
int i = i + 5;
Но это правомочно. Куда более полезным может оказаться лямбда, которая может вызывать самое себя:
#include <functional>

const std::function<std::size_t (const std::size_t)> f_fib =
[&f_fib](const std::size_t v) {
  if (v <= 1)
    return v;

  return f_fib(v - 2) + f_fib(v - 1);
};

std::cout << f_fib(25);     // 75025
Имя объекта f_fib используется в списке захвата лямбды, чтобы иметь возможность рекурсивно вызвать себя. Обернуть всё в std::function необходимо для того, чтобы избежать проблемы курицы и яйца: если написать auto, то компилятор не сможет понять во что развернуть auto, поскольку f_fib захватывает самое себя.

06 июля 2020

#13 Отсутствие автоматического преобразования указателей на указатель в указатель на указатель

В посте #6 Указатели на указатели и ссылки на указатели упоминалось, что указатели на указатели на производный класс не могут быть автоматически быть преобразованы на указатели на указатели на базовый класс. Кстати, это же и касается и случаев с увеличением количества указуемых объектов. Но почему? Что тут такого кардинально отличающегося от обычных указателей? Ведь для обычных указателей доступна такая конвертация.
Ответ начнём с того, что для стандартных типов, поддерживаемых языком, не так уж и много доступных конвертаций. Ещё меньше их для указателей. Для указателей доступно только автоматическое конвертирование из указателя на производный класс на указатель на базовый класс. Это логично. Также доступно и автоматическое ковертирование в void*. Что тоже необходимо для поддержки этой специфической идеи. Это весь (!) список доступных конвертаций. Жаловаться не приходится. Если бы интересующая нас конвертация не нарушала бы один из основополагающих принципов строгой типизации нашего с вами любимого языка, то её бы включили тоже, уж будьте уверены. Итак, перейдём непосредственно к объяснению причины:

05 июля 2020

#12 Директива using для импорта имён

Когда получаешь, больше чем хотел, ты не всегда рад
Если использовать в файле директиву using для упрощённого обращения к членам какого-нибудь пространства имён, мы получаем до конца этого файла (и всех файлов, которые его вздумают включить) возможность обращаться ко всем идентификаторам этого пространства имён прямо по конечному имени (имеется в виду неквалифицированный поиск имени, который для функций, ещё и влияет на Argument-dependent lookup), больше шансов встретить коллизию имён, косые взгляды коллег по цеху. Особенность такого объявления -- все члены этого пространства имён представляются так, будто они были объявлены в глобальной области видимости, а не в той, в которой находится директива using. А это означает, что любые локальные идентификаторы, перекрывают идентификаторы из пространства имён.

Using-объявления -- выход из ситуации. Они позволяют точечно объявить в текущей области видимости имя конкретного идентификатора из пространства имён, не засоряя именами текущую область, а так же запрещает повторно использовать идентификаторы, к которым теперь можно обращаться без квалификации доступа:

double func(const double v) {
  using std::list;
  // ...
  double list = 0.; // Ошибка. Повторное объявление list
}

01 июля 2020

#11 Вычитание указателей


... или «А что, так можно было?»
...

Наверняка вам известно, что указатели можно вычитать друг из друга. Результатом вычитания является std::ptrdiff_t. Так вот, std::ptrdiff_t основан на целочисленном типе, способным хранить отрицательные значения. Я всегда удивлялся этому, не понимал почему. А ещё я всегда старался вычитать из большего меньшее, чтобы не получить огромное переполненное значение.

19 декабря 2016

#10 Так принято

У них так принято
... или «никому нельзя трогать банан»

Чтобы вернуться после долгого отсутствия, нужно запостить что-то очень простое. Вот вам интересный факт:

окончанием 
_t в имени типа данных (wchar_t, int8_t, size_t, ptrdiff_t, ...) принято заканчивать те имена типов, которые были объявлены с помощью typedef

12 июня 2012

#9 Константные методы класса


... или «...обещаю, для тебя — никакой разницы. Ничего не изменится...»


Простое правило: если логическое состояние объекта после выполнения метода останется неизменным, то смело объявляйте его (метод) константным, а физически изменяющиеся переменные-члены класса объявляйте со спецификатором mutable.
Кстати, mutable можно применять только к нестатическим переменным-членам, что логично.
Для клиента класса не важно, изменили ли вы какую-нибудь переменную, которая для него ничего не значит. Для него важно, чтобы объект был тем же, чем был до этого, с логической точки зрения. И так, ещё раз: всегда предпочитайте физической константности логическую. И выбросьте из головы, что применение mutable — признак непрофессиональности. Это ложное наставление неопытных и глупых программистов. Все признанные мастера советуют и рекомендуют пользоваться mutable. Пример дальше.
...

11 июня 2012

#8 Указатели на функции, методы и переменные-члены


... или «очаровательные тройняшки с разным характером»


Симпатичные

Указатель на функцию


Объявляется следующим образом:
int (*pFunc)(double);
Дополнительные скобки нужны для того, чтобы дать компилятору понять, что не описывается прототип функции, именуемой pFunc, возвращающей int* и принимающей double. После объявления указателя на функцию, мы получаем переменную с именем pFunc, способную указывать на функции, сигнатура которых соответствует той, которая использовалась при объявлении указателя. Можем так же присвоить указателю нулевое значение. Однако, в отличие от указателя void*, способного ссылаться на любой тип данных, с функциями такого выполнить не удастся — нельзя создать универсальный указатель на функцию, способный ссылаться на любую функцию.
Можно так же объявить ссылку на функцию. Как и обычные ссылки, эта ссылка не может быть переназначена и должна быть инициализирована.
void someFunc(int value);
void (&rFunc)(int) = someFunc;
...