2016-03-22 7 views
11

Secondo la mia comprensione, il seguente programma dovrebbe, ovviamente, di stampa:Cosa c'è di sbagliato in questo programma che chiama un puntatore a funzione con un pacchetto di parametri?

1.0 hello world 42 

Tuttavia, non riesce a compilare. Perché?

#include <iostream> 
#include <string> 
using namespace std; 

template<class... InitialArgTypes> 
void CallWithExtraParameter(void (*funcPtr)(InitialArgTypes..., int), InitialArgTypes... initialArgs) 
{ 
    (*funcPtr)(initialArgs..., 42); 
} 

void Callee(double a, string b, int c) 
{ 
    cout << a << " " << b << " " << c << endl; 
} 

int main() 
{ 
    CallWithExtraParameter<double, string>(Callee, 1.0, string("hello world")); 
} 

Compiler output:

prog.cpp: In function 'int main()': 
prog.cpp:18:75: error: no matching function for call to 'CallWithExtraParameter(void (&)(double, std::string, int), double, std::string)' 
    CallWithExtraParameter<double, string>(Callee, 1.0, string("hello world")); 
                     ^
prog.cpp:6:6: note: candidate: template<class ... InitialArgTypes> void CallWithExtraParameter(void (*)(InitialArgTypes ..., int), InitialArgTypes ...) 
void CallWithExtraParameter(void (*funcPtr)(InitialArgTypes..., int), InitialArgTypes... initialArgs) 
    ^
prog.cpp:6:6: note: template argument deduction/substitution failed: 
prog.cpp:18:75: note: mismatched types 'int' and 'double' 
    CallWithExtraParameter<double, string>(Callee, 1.0, string("hello world")); 
                     ^
+0

hai provato a mettere doppio invece di int? –

+0

Almeno, dovrebbe essere 'CallWithExtraParameter (Callee, 1.0, string {" hello world "});'. Non dovrebbe? – skypjack

+0

@skypjack Probabilmente. (Il mio codice attuale non passa una stringa qui, ho appena scelto alcuni tipi casuali per l'MCVE) – immibis

risposta

5

In primo luogo, "hello world" non avrebbe dedurre a std::string, sarebbe dedurre a const char*, che non corrisponde Callee, quindi cerchiamo di risolvere il tuo chiamata ad essere passare "hello world"s invece.

In secondo luogo, sembra che vi sia qualche problema di avere un argomento di tipo:

void (*funcPtr)(InitialArgTypes..., int) 

Questo è apparentemente in qualche limbo tra contesto non dedotto e desumibile - nel senso che non è un contesto non dedotta (altrimenti InitialArgTypes... sarebbe stato dedotto dagli altri parametri) e non è deducibile (perché ancora non è riuscito). Quindi cerchiamo di fare un passo avanti e definitivamente lo rendono un contesto non dedotto:

template <class T> struct identity { using type = T; }; 
template <class T> using identity_t = typename identity<T>::type; 

template <class... InitialArgTypes> 
void CallWithExtraParameter(void (*funcPtr)(identity_t<InitialArgTypes>..., int), 
     InitialArgTypes... initialArgs) 
{ 
    (*funcPtr)(initialArgs..., 42); 
} 

Ora, InitialArgTypes... saranno dedurre dagli argomenti passati in alla fine. Che è quello che vogliamo, quindi questo funziona:

CallWithExtraParameter(Callee, 1.0, "hello world"s); 
+4

Non è la stringa dedotta come ' const char [N] '? – skypjack

+2

Non ho specificato esplicitamente i parametri del template come ''? – immibis

+0

@skypjack No, gli argomenti sono dedotti dal valore, non dal riferimento. – Barry

1

Il perché è stato spiegato in un'altra risposta.
Ad ogni modo, vorrei postare quello per suggerire un'altra soluzione.
Segue un esempio di lavoro:

#include <iostream> 
#include <string> 

using namespace std; 

template<class... C> 
struct Fn { 
    using type = void (*)(C..., int); 
}; 

template<class... InitialArgTypes> 
void CallWithExtraParameter(typename Fn<InitialArgTypes...>::type funcPtr, InitialArgTypes... initialArgs) 
{ 
    (*funcPtr)(initialArgs..., 42); 
} 

void Callee(double a, string b, int c) 
{ 
    cout << a << " " << b << " " << c << endl; 
} 

int main() 
{ 
    CallWithExtraParameter<double, string>(Callee, 1.0, string("hello world")); 
} 
+0

Non so perché qualcuno abbia downvoted questo; questo in realtà non è male. –

+0

Grazie per questo! – 90abyss

+0

@ T.C. In realtà non lo so, è abbastanza fastidioso vedere che le persone fanno downvotes di risposte valide senza un commento. Ad ogni modo, il tuo * non è male * è più di un uptote per me !! Grazie. – skypjack

0

Ecco una generalizzazione a qualsiasi coda dimensioni che potrebbe essere utile a voi. È anche più generico per quanto riguarda il tipo chiamabile (ad esempio, il puntatore della funzione membro viene testato anche qui).

#include <iostream> 
#include <tuple> 
#include <utility> 
#include <string> 

template <typename Callable> struct Invoke; 

template <typename R, typename... Args> 
struct Invoke<R(*)(Args...)> { 
    template <typename F, typename Tuple, std::size_t... Is, typename... As> 
    static R execute (F funcPtr, Tuple&& tuple, std::index_sequence<Is...>, As&&... as) { 
     return (*funcPtr)(std::forward<As>(as)..., std::get<Is>(std::forward<Tuple>(tuple))...); 
    } 
}; 

template <typename R, typename C, typename... Args> 
struct Invoke<R(C::*)(Args...)> { 
    template <typename F, typename Tuple, std::size_t... Is, typename... As> 
    static R execute (F f, Tuple&& tuple, std::index_sequence<Is...>, C& c, As&&... as) { 
     return (c.*f)(std::forward<As>(as)..., std::get<Is>(std::forward<Tuple>(tuple))...); 
    } 
    template <typename F, typename Tuple, std::size_t... Is, typename... As> 
    static R execute (F f, Tuple&& tuple, std::index_sequence<Is...>, C* c, As&&... as) { 
     return (c->*f)(std::forward<As>(as)..., std::get<Is>(std::forward<Tuple>(tuple))...); 
    } 
}; 

template <typename R, typename C, typename... Args> 
struct Invoke<R(C::*)(Args...) const> { 
    template <typename F, typename Tuple, std::size_t... Is, typename... As> 
    static R execute (F f, Tuple&& tuple, std::index_sequence<Is...>, C& c, As&&... as) { 
     return (c.*f)(std::forward<As>(as)..., std::get<Is>(std::forward<Tuple>(tuple))...); 
    } 
    template <typename F, typename Tuple, std::size_t... Is, typename... As> 
    static R execute (F f, Tuple&& tuple, std::index_sequence<Is...>, const C* c, As&&... as) { 
     return (c->*f)(std::forward<As>(as)..., std::get<Is>(std::forward<Tuple>(tuple))...); 
    } 
}; 

template <typename Functor> 
struct Invoke : Invoke<decltype(&Functor::operator())> {}; 
// etc... 

template <typename R = void, typename F, typename Tuple, typename... Args> 
R invokeWithTupleTail (F funcPtr, Tuple&& tuple, Args&&... args) { 
    return Invoke<F>::execute(funcPtr, std::forward<Tuple>(tuple), 
     std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>{}, std::forward<Args>(args)...); 
} 

// Testing 
struct Thing { 
    int call (char k, int n, double a, std::string b, int c) { 
     std::cout << k << ' ' << n << ' ' << a << ' ' << b << ' ' << c << '\n'; 
     return 5; 
    } 
    int doIt (char k, int n, double a, std::string b, int c) const { 
     std::cout << k << ' ' << n << ' ' << a << ' ' << b << ' ' << c << '\n'; 
     return 12; 
    } 
    int operator() (char k, int n, double a, std::string b, int c) const { 
     std::cout << k << ' ' << n << ' ' << a << ' ' << b << ' ' << c << '\n'; 
     return 20; 
    } 
}; 

void foo (char k, int n, double a, std::string b, int c) { 
    std::cout << k << ' ' << n << ' ' << a << ' ' << b << ' ' << c << '\n'; 
} 

int bar (char k, int n, double a, std::string b, int c) { 
    std::cout << k << ' ' << n << ' ' << a << ' ' << b << ' ' << c << '\n'; 
    return 10; 
} 

int main() { 
    const auto tupleTail = std::make_tuple(1.5, std::string("hello"), 42); 
    invokeWithTupleTail(foo, tupleTail, 'a', 8); // a 8 1.5 hello world 42 
    int a = invokeWithTupleTail<int>(&bar, tupleTail, 'a', 8); // a 8 1.5 hello world 42 
    std::cout << a << '\n'; // 10 

    Thing thing; 
    a = invokeWithTupleTail<int>(&Thing::call, tupleTail, thing, 'a', 8); // a 8 1.5 hello world 42 
    std::cout << a << '\n'; // 5 
    a = invokeWithTupleTail<int>(&Thing::doIt, tupleTail, &thing, 'a', 8); // a 8 1.5 hello world 42 
    std::cout << a << '\n'; // 12 
    a = invokeWithTupleTail<int>(&Thing::operator(), tupleTail, thing, 'a', 8); // a 8 1.5 hello world 42 
    std::cout << a << '\n'; // 20 
} 
Problemi correlati