Единственным вложенным элементом, который библиотека TTI не интроспектирует, являются шаблоны функций.
Шаблоны функций, как и функции, могут быть шаблонами функций членов или статическими шаблонами функций членов. В этом отношении они связаны с функциями. Шаблоны функций представляют собой семейство возможных функций. В этом отношении они похожи на шаблоны классов, которые представляют собой семейство возможных типов классов.
Техника интроспекции шаблонов классов в библиотеке TTI взята из реализации методики в библиотеке Boost MPL. В случае<BOOST_TTI_HAS_TEMPLATE
>он непосредственно использует функциональность библиотеки Boost MPL, в то время как в случае<BOOST_TTI_HAS_TEMPLATE_CHECK_PARAMS
>он воспроизводит большую часть техники в библиотеке Boost MPL. Техника напрямую зависит от того, что в C++ мы можем передать шаблон в качестве параметра другому шаблону, используя так называемый тип параметра шаблона.
Одна очевидная вещь о типе параметров шаблона шаблона заключается в том, что это шаблон класса. По каким бы то ни было историческим или техническим причинам, никто никогда не предполагал, что C++ имеет способ передачи шаблона функций непосредственно в качестве параметра шаблона, возможно, его можно назвать типом параметра «шаблона шаблона функций». Лично я думаю, что это будет хорошим дополнением к C++ и сделает возможность передачи шаблона в качестве параметра другому шаблону более ортогональной, поскольку будут поддерживаться как шаблоны классов, так и шаблоны функций. Мои усилия по обсуждению этого на основных новостных группах C++ столкнулись с аргументами как против его практического использования, так и с обоснованием того, что можно передать шаблон функции другому шаблону, вложенному в класс без шаблона, который служит типом. Но, конечно, мы можем сделать то же самое с шаблонами классов, что фактически и делает Boost MPL для передачи шаблонов в виде метаданных, но у нас все еще есть параметры шаблонов шаблонов в виде шаблонов классов.
Тем не менее, тот факт, что мы можем передавать шаблоны классов в качестве параметра шаблона, но не шаблоны функций в качестве параметра шаблона, является основным фактором, почему нет действительно хорошего метода для самоанализа шаблонов функций во время компиляции.
Однако существует альтернативный, но менее определенный способ интроспекции шаблона функций. Я постараюсь объяснить, почему этот способ в настоящее время не включен в библиотеку ТТИ, но сначала я объясню, что это такое.
Можно проверить, существует ли какая-либо конкретная инстанциациявложенного шаблона функций во время компиляции без возникновения ошибки компилятора. Хотя проверка того, существует ли какая-либо конкретная инстанциация вложенного шаблона функций во время компиляции, не доказывает, что вложенный шаблон функций сам по себе существует или не существует, поскольку сам инстанциация может быть неправильной и потерпеть неудачу, даже когда вложенный шаблон функций существует, он обеспечивает частичное, если некорректное, средство проверки.
Код для этого для шаблонов функций-членов выглядит так (подобный код также существует для шаблонов статических функций-членов):
template
<
class C,
class T
>
struct TestFunctionTemplate
{
typedef char Bad;
struct Good { char x[2]; };
template<T> struct helper;
template<class U> static Good check(helper<&U::template SomeFuncTemplateName<int,long,double> > *);
template<class U> static Bad check(...);
static const bool value=sizeof(check<C>(0))==sizeof(Good);
};
где «SomeFuncTemplateName» — название вложенного шаблона функций, за которым следуют некоторые параметры для его реализации. «Класс С» является типом ограждающего класса, а «Класс Т» является типом инстанцированного шаблона функции члена в качестве функции члена.
В качестве примера, если бы мы имели:
struct AType
{
template<class X,class Y,class Z> double SomeFuncTemplateName(X,Y *,Z &) { return 0.0; }
};
Затем инстанцирует вышеупомянутый шаблон с:
TestFunctionTemplate
<
AType,
double (AType::*)(int,long *,double &)
>
Это дало бы нам значение Boolean, которое указывало бы, существует ли шаблон вложенных функций для конкретной инстанциации, представленной выше. Кроме того, с помощью макроса библиотека TTI может предоставить средства для указания названия вложенного шаблона функций-членов («SomeFuncTemplateName» выше) и его набора мгновенных параметров («int,long,double» выше) для генерации шаблона.
Так почему же библиотека TTI не предоставляет, по крайней мере, такой большой функционал для интроспекции шаблонов функций-членов, даже если она представляет собой частично ошибочный способ сделать это?
Причина ошеломляюще разочаровывает. Хотя вышеупомянутый код является совершенно правильным кодом C++ («кланг» работает правильно), два основных компилятора C++ во всех своих различных выпусках не могут правильно обрабатывать вышеупомянутый код. Как gcc (g++), так и Visual C++ неправильно выбирают неправильную функцию «проверки», даже когда применяется правильная функция «проверки» (Comeau C++ также терпит неудачу, но меня меньше беспокоит этот компилятор, поскольку он не используется почти так же, как два других). Все мои попытки найти альтернативу вышеупомянутому коду также провалились. Проблемы с обоими компиляторами в этом отношении легче увидеть с помощью этого фрагмента:
struct AType
{
template<class AA> void SomeFuncTemplate() { }
};
template<class T>
struct Test
{
template<T> struct helper;
template<class U> static void check(helper<&U::template SomeFuncTemplate<int> > *) { }
};
int main()
{
Test< void (AType::*)() >::check<AType>(0);
return 0;
}
Оба компилятора сообщают об ошибках компиляции с этим совершенно правильным кодом.
gcc:
error: no matching function for call to 'Test<void (AType::*)()>::check(int)'
и msvc:
error C2770: invalid explicit template argument(s) for 'void Test<T>::check(Test<T>::helper<&U::SomeFuncTemplate<int>> *)'
Для этих задач компилятора существует обходной путь, который заключается в том, чтобы жестко закодировать имя прилагающегося класса через макрос в генерируемом шаблоне, а не передавать его как тип шаблона. В этом случае оба компилятора могут правильно обрабатывать как код функции члена, так и фрагмент кода выше. По сути, когда линия:
template<class U> static void check(helper<&U::template SomeFuncTemplate<int> > *) { }
заменяется:
template<class U> static void check(helper<&AType::template SomeFuncTemplate<int> > *) { }
GCC и Visual C++ работают корректно. То же самое касается линии «проверить» в шаблоне «TestFunction» выше.
Но обходной путь разрушает один из основных принципов библиотеки TTI, который заключается в том, что класс, содержащийся в приложении, должен быть передан в качестве параметра шаблона, тем более что класс, содержащийся в нем, на самом деле не должен существовать (см.<BOOST_TTI_MEMBER_TYPE
>и предыдущее обсуждение «Необходимых типов»), не создавая ошибки компилятора. Поэтому я решил не внедрять даже эту методологию, чтобы интроспектировать вложенные шаблоны функций в библиотеке TTI.