вторник, 22 декабря 2009 г.

Template keyword

Как вы думаете, что нужно написать, чтобы вызвать шаблонную фукнцию шаблонного класса из шаблонной фукнции?

Чтобы было понятно о чем идет речь, рассмотрим конкретный пример:
template<typename T> struct A {
template<int I> void f() {}
};

template<typename TT> void g()
{
A<TT> a;
a.f<3>(); // error is here
}
Код выглядит корректным, однако, строка, где указана ошибка, рассматривается компилятором как выражение со знаком меньше. Интересно, что Visual C++ 2008 догадывается, что там на самом деле (другие компиляторы не настолько догадливы). Однако стандарт С++ говорит о том, что нужно явно указывать компилятору на наличие вызова шаблонной функции там. Об этом говорит пункт 14.2/4. Правильная запись показана ниже:
template<typename TT> void g()
{
A<TT> a;
a.template f<3>(); // correct
}
Слово template выглядит довольно неожиданно в этом месте, что приводит в замешательство не только новичков. Тем более, что Visual C++ игнорирует стандарт по непонятной причине. В будущем стандарте требование ключевого слова остается.

C++'03 Standard 14.2/4:
When the name of a member template specialization appears after . or -> in a postfix-expression, or after nested-name-specifier in a qualified-id, and the postfix-expression or qualified-id explicitly depends on a template-parameter (14.6.2), the member template name must be prefixed by the keyword template. Otherwise the name is assumed to name a non-template.

вторник, 8 декабря 2009 г.

Delayed writes

Наверное, уже многие знают, что результат выражения i++ + ++i является неопределенным. Тут вроде все ясно: в operator+ передаются два аргумента и порядок вычисления аргументов может быть любым, что и дает неопределенность. Однако, в стандарте есть гораздо более интересные примеры. Утверждается (С++'03 5/4), что:
i = v[i++]; // the behavior is unspecified
i = 7, i++, i++; // i becomes 9

i = ++i + 1; // the behavior is unspecified
i = i + 1; // the value of i is incremented
Рассмотрим предпоследнее выражение. Допустим, изначально i=0. Интересно, что интуитивно выражение i = ++i + 1 абсолютно определено и непонятно как создатели компилятора могут выдать что-то кроме 2. Однако, стандарт позволяет в реализации делать отложенную запись вычислений. Т. е. в этом последнем примере может быть такая реализация:
  1. Посчитали значение ++i и запомнили в регистре (в переменную i результат запишем позднее).
  2. Считаем результат ++i + 1, взяв результат п. 1.
  3. Записываем в i значение, которое посчитали в п. 2.
  4. О, чуть не забыли. Записываем результат п. 1 в переменную i.
Итого, получили в i значение 1. Такое поведение допускается стандартом в пункте 5 абзац 4:
Between the previous and next sequence point a scalar object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be accessed only to determine the value to be stored. The requirements of this paragraph shall be met for each allowable ordering of the subexpressions of a full expression; otherwise the behavior is undefined.
В отладочном режиме такого, скорее всего не будет, но при оптимизации очень может быть. Для разработчиков это означает, что, если в выражении переменная меняется более одного раза, то это скорее всего приведет к нежелательным последствиям и трудноуловимым багам.

Ссылки по теме:
Why is `i = ++i + 1` unspecified behavior?
Точки следования (sequence points)

четверг, 3 декабря 2009 г.

How to tame the Windows headers


При подключении windows.h определяется огромное количество макросов, что бывает совсем не полезно. Для борьбы с этим в Microsoft придумали дефайны, которые позволяют немного ограничить объем подключаемой информации. В MSDN эта тема обходится стороной, поэтому ниже приведен фрагмент windows.h:
/*  If defined, the following flags inhibit definition
*     of the indicated items.
*
*  NOGDICAPMASKS     - CC_*, LC_*, PC_*, CP_*, TC_*, RC_
*  NOVIRTUALKEYCODES - VK_*
*  NOWINMESSAGES     - WM_*, EM_*, LB_*, CB_*
*  NOWINSTYLES       - WS_*, CS_*, ES_*, LBS_*, SBS_*, CBS_*
*  NOSYSMETRICS      - SM_*
*  NOMENUS           - MF_*
*  NOICONS           - IDI_*
*  NOKEYSTATES       - MK_*
*  NOSYSCOMMANDS     - SC_*
*  NORASTEROPS       - Binary and Tertiary raster ops
*  NOSHOWWINDOW      - SW_*
*  OEMRESOURCE       - OEM Resource values
*  NOATOM            - Atom Manager routines
*  NOCLIPBOARD       - Clipboard routines
*  NOCOLOR           - Screen colors
*  NOCTLMGR          - Control and Dialog routines
*  NODRAWTEXT        - DrawText() and DT_*
*  NOGDI             - All GDI defines and routines
*  NOKERNEL          - All KERNEL defines and routines
*  NOUSER            - All USER defines and routines
*  NONLS             - All NLS defines and routines
*  NOMB              - MB_* and MessageBox()
*  NOMEMMGR          - GMEM_*, LMEM_*, GHND, LHND, associated routines
*  NOMETAFILE        - typedef METAFILEPICT
*  NOMINMAX          - Macros min(a,b) and max(a,b)
*  NOMSG             - typedef MSG and associated routines
*  NOOPENFILE        - OpenFile(), OemToAnsi, AnsiToOem, and OF_*
*  NOSCROLL          - SB_* and scrolling routines
*  NOSERVICE         - All Service Controller routines, SERVICE_ equates, etc.
*  NOSOUND           - Sound driver routines
*  NOTEXTMETRIC      - typedef TEXTMETRIC and associated routines
*  NOWH              - SetWindowsHook and WH_*
*  NOWINOFFSETS      - GWL_*, GCL_*, associated routines
*  NOCOMM            - COMM driver routines
*  NOKANJI           - Kanji support stuff.
*  NOHELP            - Help engine interface.
*  NOPROFILER        - Profiler interface.
*  NODEFERWINDOWPOS  - DeferWindowPos routines
*  NOMCX             - Modem Configuration Extensions
*/