2015-04-10 2 views
5

Что я пытаюсь сделать: у меня есть объект-шаблон, который, как часть интерфейса, должен иметь функцию «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), но я не вижу, как использовать его, когда я не знаю точной подписи (т.е. я не знаю количество и типы статических параметров). Я могу отладочный заставить их для конкретной задачи под рукой, конечно, но ...

Так у меня есть два вопроса:

  1. Почему это должно быть всегда так трудно с MSVC .. Хорошо, это риторический вопрос, ответ не нужен.
  2. Я злоупотребляю некоторой добротой clang/gcc и на самом деле MSVC делает правильные вещи, бросая бессмысленные ошибки на моем лице? Возможны ли какие-либо обходные пути или правильные способы сделать это, кроме грубого форсирования всех возможных комбинаций параметров статического/шаблона и их сравнения с использованием прототипа полной подписи?

ответ

1

Проблема заключается в том, что MSVC отказывается извлекать тип &C::template process<_D> для использования в разрешении перегрузки (обратите внимание на отсутствие значимого сообщения об ошибке). По-видимому, он считает, что инстанцированная функция является множеством перегрузки только одной функции; вероятно, реализация goof.

Вы можете заставить его преобразовать перегрузки установлен тип указателя на функцию путем подачи его в качестве параметра функции:

template<typename T> 
T* fn_id(T* func) { 
    return nullptr; 
} 

После T был уплощенным в тип функции, вы можете использовать его в decltype ,

template <typename C> 
    static R<1> retrieve(decltype(fn_id(&C::template process<_D>))); 

С этим взломом я получаю тот же результат, что и с clang.

+0

Я попытался использовать подход грубой силы на всякий случай и был удивлен ошибками «двусмысленного вызова» для процесса (int, T) с проверкой кандидатов на процесс без шаблонов (int, int) и процесс (int, T). Это означает, что он пытался вывести параметры шаблона не из вызова, а из преобразования в указатель на функцию. Кажется, что это также нормально с ключевым словом «шаблон», отсутствующим в проверках выше (другие компиляторы этого не делают). Все это подкрепляет вашу теорию - звучит, как работа отложенного шаблона MS, здесь. Но ваша идея работает как шарм, спасибо большое! –

+0

В качестве примечания: он также работает и для clang, поэтому его можно использовать в качестве решения кросс-компилятора, когда рассматривается msvc2012. И реализация fn_id не требуется. –

+0

Добро пожаловать, это была интересная проблема для решения :) – slaphappy

Смежные вопросы