Быстрые делегаты C++ - Смягчение проверки соответствия типов
ОГЛАВЛЕНИЕ
Смягчение проверки соответствия типов
Типы параметров шаблона, передаваемых делегату, проверяются намного строже, чем нужно в реальности. Нам может потребоваться работать с группой функций int (*)(int) и функций int (*)(long) совместно. Когда эти функции присваиваются в fd::delegate1 < int, int >, компилятор выдаёт ошибку для функций int (*)(long),int' и 'long' являются различными типами. говорящую, что их присваивание невозможно, так как '
Определив макрос FD_TYPE_RELAXATION перед включением "delegate.h" можно включить смягчение проверки соответствия типов. В двух словах, функция (свободная функция, функция-член или функтор) может быть присвоена или связана с fd::delegate во всех случаях, когда выполняются три следующих условия:
- Число аргументов согласовано.
- Каждый соответствующий аргумент заведомо может быть преобразован (из аргумента делегата в аргумент целевой функции) компилятором.
- Возвращаемый тип заведомо может быть преобразован (из возвращаемого типа делегата в возвращаемый тип целевой функции, и наоборот) в компилятор.
Если любое из данных условий не выполняется, компилятор сообщит об этом (предупреждение и/или сообщения об ошибке во время компиляции).
CBase1 b1;
//
// int CBase1::foo(int) const;
// int CBase1::bar(int);
//
fd::delegate1 < int, long > dg1(&CBase1::foo, &b1);
dg1(123);
Вышеприведенное определение делегата теоретически эквивалентно следующему определению функции:
CBase1 b1;
int fd_delegate1_dg1(long l)
{
return b1.foo(l);
}
fd_delegate1_dg1(123);
Это показывает, почему должны выполняться три условия для fd::delegate, чтобы он работал в режиме смягченной проверки типов.
// предупреждение компиляции! :преобразование 'return' : из '' to '', возможна потеря данных
fd::delegate1 < float, long > dg2(&CBase1::bar, &b1);
эквивалентно:
float fd_delegate1_dg2(long l)
{
// предупреждение компиляции! : возможна потеря данных
return b1.bar(l);
}
Возвращаемый тип 'int' может быть преобразован в возвращаемый тип 'float' без проблем, но преобразование возвращаемого типа 'float' в возвращаемый тип 'int' вызовет предупреждение о возможной потере данных.
// ошибка компиляции! : невозможно преобразовать параметр 3 из 'char *' в 'int'
fd::delegate1 < int, char * > dg3(&CBase1::foo, &b1);
эквивалентно:
int fd_delegate1_dg3(char * ch)
{
// ошибка компиляции! : невозможно преобразовать параметр 'ch' из 'char *' в 'int'
return b1.foo(ch);
}
и компилятор выдаст ошибку, так как 'char *' заведомо нельзя преобразовать в 'int'.
CDerived1 d1("d1");
//
// class CDerived1 : public CBase1 { };
//
fd::delegate2 < int, CDerived1 *, long > dg5(&CBase1::bar);
эквивалентно:
int fd_delegate2_dg5(CDerived1 * pd1, long l)
{
return pd1->bar(l);
}
и выброс от 'CDerived1 *' в 'CBase1 *' всегда безопасен, поэтому преобразование заведомо возможно.
fd::make_delegate() для режима смягченной проверки соответствия типов
[Устаревшее] Когда определяется FD_TYPE_RELAXATION, у наборов fd::make_delegate() включается поддержка этого режима. Так как fd::make_delegate() не может догадаться, какой тип делегата необходимо создать, вызывающий должен задать пустой указатель типа делегата как первый аргумент fd::make_delegate(). Аналогичная идея используется в версии адаптера функции-члена fd::make_delegate(),[Устарело] описанной раньше.
Целью использования make_delegate() является автоматическое выведение параметра шаблона, поэтому нет смысла использовать make_delegate, если дополнительная информация о типе обязательно предоставляется в качестве первого аргумента. Так как эта функция плохо работает в плохих компиляторах, таких как VC6, она удалена в новой версии.
static_assert (поддержка отладки)
Когда делегат присваивается или связывается с указателем функции, компилятор генерирует соответствующий вызов функции operator () во время компиляции. Если есть предупреждение или ошибка несоответствия типов данных, бывает сложно отследить, в каком месте она возникла. Компилятор с развитой логикой, такой как VC7.1, имеет удобную функцию отслеживания данных предупреждений или ошибок с подробной информацией о типе шаблона в пределах исходного кода пользователя, но VC6 не имеет этой возможности. (VC6 обычно предоставляет два уровня отслеживания.) Поэтому мы попытались поместить static_assert (FD_STATIC_ASSERT, FD_PARAM_TYPE_CHK) в как можно большее число мест, чтобы было легче отследить источник ошибок/предупреждений в исходном коде пользователя.