Что я пытаюсь сделать: у меня есть объект-шаблон, который, как часть интерфейса, должен иметь функцию «process», определенную с помощью количество аргументов (я не знаю, сколько), некоторые из которых являются аргументами шаблона.MSVC 2012 обнаружение числа аргументов шаблона функции шаблона через SFINAE
I.e.
struct A { static void process(int a); };
struct B { template <typename B0> static void process(int a, B0 b0); };
являются действительными обработчиками для приема. Поэтому теперь мне нужно обнаружить подпись для обработчика: параметры с типичным типом и ряд параметров шаблона.
Для этого я использую несколько шаблонов магии, которые могут быть сужены до проблемной части - обнаружение нескольких шаблонных аргументов (или просто получение шаблонной подписи).
Так я пытаюсь выяснить необходимую информацию, проверяя для явно специализированной подписи с использованием метода, описанного в Is it possible to write a template to check for a function's existence?
struct _D;
template <typename T>
struct get_template_args_count
{
private:
template <int count> struct R { enum { value = count }; };
template <typename C>
static R<0> retrieve(decltype(&C::process));
template <typename C>
static R<1> retrieve(decltype(&C::template process<_D>));
template <typename C>
static R<-1> retrieve(...);
public:
typedef decltype(retrieve<T>(nullptr)) Result;
enum { value = Result::value };
};
int main(int argc, char* argv[])
{
std::cout
<< "A::process " << get_template_args_count<A>::value << "\n"
<< "B::process " << get_template_args_count<B>::value << "\n";
std::cin.get();
return 0;
}
С лязгом (построен с msvc2013 или Linux версии, построенной с помощью GCC -4.9.2) компилирует и выходы:
A::process 0
B::process 1
С msvc2012 он компилирует тоже, но выходы:
A::process 0
B::process -1
Когда загнали в закомментировав случай запасной (тот, с (...)) msvc2012 выкрутасы:
main.cpp(28): error C2893: Failed to specialize function template 'get_template_args_count<T>::R<count> get_template_args_count<T>::retrieve(unknown)'
with [ T=B, count=1 ]
With the following template arguments: 'B'
v:\test\test\test\main.cpp(63) : see reference to class template instantiation 'get_template_args_count<T>' being compiled
with [ T=B ]
main.cpp(28): error C2893: Failed to specialize function template 'get_template_args_count<T>::R<count> get_template_args_count<T>::retrieve(unknown)'
with [ T=B, count=0 ]
With the following template arguments: 'B'
main.cpp(29): error C2825: 'get_template_args_count<T>::Result': must be a class or namespace when followed by '::'
with [ T=B ]
main.cpp(29): error C2039: 'value' : is not a member of '`global namespace''
main.cpp(29): error C2275: 'get_template_args_count<T>::Result' : illegal use of this type as an expression
with [ T=B ]
main.cpp(29): error C2146: syntax error : missing '}' before identifier 'value'
main.cpp(29): error C2143: syntax error : missing ';' before '}'
main.cpp(29): error C2365: 'value' : redefinition; previous definition was 'enumerator'
main.cpp(29) : see declaration of 'value'
(бревно немного переформатировать брать меньше строк)
Я ve также попытался использовать различные методы, описанные в комментариях к вышеперечисленному вопросу (используя char [sizeof], используя typeof и перемещая проверку в тип возврата), безрезультатно - они либо дают одинаковые результаты, либо разваливаются даже с более страшными ошибками (включая «неожиданный конец файла» без очевидной причины).
Я также проверил аналогичный вопрос Deduce variadic args and return type from functor template parameter (MSVC-specific) с другой методикой (сравнивая прототипы через SFINAE), но я не вижу, как использовать его, когда я не знаю точной подписи (т.е. я не знаю количество и типы статических параметров). Я могу отладочный заставить их для конкретной задачи под рукой, конечно, но ...
Так у меня есть два вопроса:
- Почему это должно быть всегда так трудно с MSVC .. Хорошо, это риторический вопрос, ответ не нужен.
- Я злоупотребляю некоторой добротой clang/gcc и на самом деле MSVC делает правильные вещи, бросая бессмысленные ошибки на моем лице? Возможны ли какие-либо обходные пути или правильные способы сделать это, кроме грубого форсирования всех возможных комбинаций параметров статического/шаблона и их сравнения с использованием прототипа полной подписи?
Я попытался использовать подход грубой силы на всякий случай и был удивлен ошибками «двусмысленного вызова» для процесса (int, T) с проверкой кандидатов на процесс без шаблонов (int, int) и процесс (int, T). Это означает, что он пытался вывести параметры шаблона не из вызова, а из преобразования в указатель на функцию. Кажется, что это также нормально с ключевым словом «шаблон», отсутствующим в проверках выше (другие компиляторы этого не делают). Все это подкрепляет вашу теорию - звучит, как работа отложенного шаблона MS, здесь. Но ваша идея работает как шарм, спасибо большое! –
В качестве примечания: он также работает и для clang, поэтому его можно использовать в качестве решения кросс-компилятора, когда рассматривается msvc2012. И реализация fn_id не требуется. –
Добро пожаловать, это была интересная проблема для решения :) – slaphappy