Why does the variadic template argument deduction fail for this function pointer?

501 views Asked by At

In the following minimal example, S::foo works, but S::bar fails.

The only difference is the order of the parameter packs Ts and Us.

struct FPtr and S::lol are the best workaround I've found, but it's rather uncomfortable to use in practice.

Why does the argument deduction for bar fail (especially since I've explicitly specified the types, so no deduction should happen at all)? Is this a compiler bug (occurs with clang++ 3.5 and g++ 4.9), or is this in the standard, for some reason?

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);
}

The error message is:

$ 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 ...)) {}
             ^

Note: I have reported this bug on the GCC and LLVM bug trackers.

1

There are 1 answers

2
David G On BEST ANSWER

I've tested this code with both Clang and GCC and they both fail to compile the program. I'd say this is a bug in both compilers. A function parameter pack which occurs before the end of the parameter-list is a non-deduced context. After substituting the explicitly specified template argument(s) it should build the function

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

which should match the call. Clang and GCC have had problems in areas like this before and their diagnostics are known to have been less than helpful. Surprisingly however VC++ compiles the code.

Consider the following which works under both compilers.

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

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

Your program has the same semantics and should be treated equally.