2016-03-22 8 views
11

aşağıdaki program belli baskı olmalıdır:Bu programın bir parametre işaretiyle bir işlev işaretçisini çağırması yanlış mı? benim anlayışıma göre

1.0 hello world 42 

Ancak, derlemek için başarısız olur. Niye ya? İlk

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

int yerine double koymayı denediniz mi? –

+0

En azından, CallWithExtraParameter (Callee, 1.0, string {"hello world"}); 'olmalıdır. Yapmamalı mı? – skypjack

+0

@skypjack Muhtemelen. (Benim gerçek kodum burada bir dizgeyi geçirmiyor, sadece MCVE için bazı rasgele türler seçtim) – immibis

cevap

5

Compiler output:

#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")); 
} 
, "hello world" std::string için anlamak olmaz, bu Callee uymuyor ki const char* için anlamak, o yüzden olabilir yerine "hello world"s geçmek çağrınızı düzeltmek izin vermedi.

İkincisi, türünde bir argüman sahip bazı sorunu olduğu görülmektedir:

olmayan çıkarılabilir bağlam ve anlaşılabilir arasında bazı belirsizlik içinde görünüşe göre
void (*funcPtr)(InitialArgTypes..., int) 

- o olmayan çıkarılabilir bağlam olmadığını (aksi halde InitialArgTypes... diğer parametrelerden çıkarılabilirdi) ve bu sonuç çıkarılabilir değil (çünkü hala başarısız oldu). Yani bir adım daha ileri giderek kesin olmayan bir çıkarılabilir bağlamı yapalım:

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

Şimdi, InitialArgTypes... sonunda geçirilen tartışmalardan çıkarılabilecek edilecektir. Hangi bizim istediğimiz, bu nedenle bu işleri:

başka bir yanıt olarak açıklandığı neden
CallWithExtraParameter(Callee, 1.0, "hello world"s); 
+4

" const char [N] '? – skypjack

+2

Şablon parametrelerini açık olarak 'olarak belirtmedim mi? – immibis

+0

@skypjack Hayır, yayınlar referans ile değil, değerle çıkarılır. – Barry

1

.
Her neyse, bir çözüm daha önermek için bunu göndermek istiyorum.
bir çalışma örneği aşağıdaki gibidir:

#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

Birinin neden bunu reddettiğinden emin değil; Bu aslında kötü değil. –

+0

Bunun için teşekkürler! – 90abyss

+0

@ T.C. Aslında bilmiyorum, insanların yorum yapmadan geçerli yanıtları düşürdüğünü görmek oldukça can sıkıcı. Her neyse, senin * kötü değil * benim için bir daha fazlası var! Teşekkür ederim. – skypjack

0

İşte size yararlı olabilecek herhangi bir boyut kuyruğuna bir genelleme. Ayrıca, çağrılabilir tipte daha geneldir (örn. Üye fonksiyon gösterici burada da test edilir).

#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 
}