Почему для этого указателя на функцию не удается вычислить аргумент вариативного шаблона?

В следующем минимальном примере S::foo работает, но S::bar не работает.

Единственная разница - это порядок пакетов параметров Ts и Us.

struct FPtr и S::lol - лучший обходной путь, который я нашел, но его довольно неудобно использовать на практике.

Почему вывод аргумента для bar терпит неудачу (тем более, что я явно указал типы, поэтому никакого вывода не должно происходить вообще)? Это ошибка компилятора (встречается с clang++ 3.5 и g++ 4.9) или по какой-то причине это стандарт?

template<typename ... Ts>
struct FPtr {
    FPtr(void (*val)(Ts ...)) : val{val} {}

    void (*val)(Ts ...);
};


template<typename ... Ts>
struct S {
    template<typename ... Us>
    void lol(FPtr<Us ..., Ts ...>) {}

    template<typename ... Us>
    void foo(void (*)(Ts ..., Us ...)) {}

    template<typename ... Us>
    void bar(void (*)(Us ..., Ts ...)) {}
};


void f(int, float) {}
void g(float, int) {}


int main() {
    S<int> s;

    s.lol<float>(FPtr<float, int>(g));
    s.foo<float>(f);
    s.bar<float>(g);
}

Сообщение об ошибке:

$ clang++ -std=c++14 t27.cpp -Wall -Wextra -pedantic
t27.cpp:31:4: error: no matching member function for call to 'bar'
        s.bar<float>(g);
        ~~^~~~~~~~~~
t27.cpp:18:7: note: candidate template ignored: failed template argument deduction
        void bar(void (*)(Us ..., Ts ...)) {}
             ^

Примечание. Я сообщил об этой ошибке в GCC и LLVM для отслеживания ошибок.


person mic_e    schedule 25.06.2015    source источник
comment
Возможно, g ++ 5.1 исправит эту проблему.   -  person Ron Tang    schedule 25.06.2015
comment
Пример можно упростить до этого.   -  person Jarod42    schedule 25.06.2015
comment
@ Jarod42 ведет себя иначе; он компилируется в обоих компиляторах, если не полагается на авто-дедукцию.   -  person mic_e    schedule 25.06.2015


Ответы (1)


Я тестировал этот код как с Clang, так и с GCC, и они оба не смогли скомпилировать программу. Я бы сказал, что это ошибка обоих компиляторов. Пакет параметров функции, который находится перед концом списка параметров, является невыявленным контекстом. После замены явно указанного аргумента (ов) шаблона он должен построить функцию

template<>
S<int>::bar(void (*)(float, int));

который должен соответствовать вызову. У Clang и GCC раньше были проблемы в подобных областях, и их диагностика, как известно, была менее чем полезной. Как ни странно, VC ++ компилирует код.

Рассмотрим следующее, которое работает с обоими компиляторами.

template<class... Ts>
struct S {
    template<class... Us>
    void bar(Us..., Ts...);
};

int main() {
    S<int>().bar<int>(1, 2);
}

У вашей программы такая же семантика, и к ней следует относиться одинаково.

person 0x499602D2    schedule 25.06.2015
comment
Как у вашего кода такая же семантика, что и у меня? Ваш не передает указатель на функцию. Я столкнулся с этой проблемой только с указателями на функции. Технически обе наши программы изоморфны int main();, но я не зря передаю этот указатель на функцию ... - person mic_e; 25.06.2015
comment
@mic_e Я привожу пример того, как ваша программа ведет себя аналогичным образом. Вывод аргументов шаблона не выполняется, поэтому мой конструктор имеет подпись S(int, int), точно так же, как ваш конструктор должен иметь S(void (*)(float, int)) при подстановке аргументов шаблона. - person 0x499602D2; 25.06.2015