2016-02-22 7 views
13

wir ausgewählt sagen, dass ich etwas willkürlich kompliziert überladene Funktion haben:Bestimmung, die wurde überlasten

template <class T> void foo(T&&); 
template <class T> void foo(T*); 
void foo(int); 

I, für einen bestimmten Ausdruck, diefoo() wird aufgerufen, wissen wollen. Zum Beispiel einige Makro WHICH_OVERLOAD gegeben:

using T = WHICH_OVERLOAD(foo, 0);  // T is void(*)(int); 
using U = WHICH_OVERLOAD(foo, "hello"); // U is void(*)(const char*); 
// etc. 

Ich weiß nicht, wo ich so etwas nutzen würde - ich bin nur neugierig, ob es möglich ist.

+3

Beachten Sie, dass der Typ Überlastung nicht genug ist btw für unterscheiden. – Jarod42

+5

Ich glaube nicht, dass das möglich ist. Dies ist sehr ähnlich zu den Aufrufeigenschaften der Bibliotheksgrundlagen TS, von denen bekannt ist, dass sie die Implementierung von Compilermagie erfordern. –

+0

@ T.C. Ah, ja, das habe ich mir gedacht. [Dieser] (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3866.html)? – Barry

Antwort

0

Ich bin wahrscheinlich weit von dem, was Sie im Kopf haben, aber ich habe meine Zeit auf, dass verbracht und es lohnt sich, eine Antwort (vielleicht eine völlig falsche, in der Tat) hinzuzufügen:

#include<type_traits> 
#include<utility> 

template <class T> void foo(T&&); 
template <class T> void foo(T*); 
void foo(int); 

template<int N> 
struct choice: choice<N+1> { }; 

template<> 
struct choice<3> { }; 

struct find { 
    template<typename A> 
    static constexpr 
    auto which(A &&a) { 
     return which(choice<0>{}, std::forward<A>(a)); 
    } 

private: 
    template<typename A> 
    static constexpr 
    auto which(choice<2>, A &&) { 
     // do whatever you want 
     // here you know what's the invoked function 
     // it's template<typename T> void foo(T &&) 
     // I'm returning its type to static_assert it 
     return &static_cast<void(&)(A&&)>(foo); 
    } 

    template<typename A> 
    static constexpr 
    auto which(choice<1>, A *) { 
     // do whatever you want 
     // here you know what's the invoked function 
     // it's template<typename T> void foo(T *) 
     // I'm returning its type to static_assert it 
     return &static_cast<void(&)(A*)>(foo); 
    } 

    template<typename A> 
    static constexpr 
    auto 
    which(choice<0>, A a) 
    -> std::enable_if_t<not std::is_same<decltype(&static_cast<void(&)(A)>(foo)), decltype(which(choice<1>{}, std::forward<A>(a)))>::value, decltype(&static_cast<void(&)(A)>(foo))> 
    { 
     // do whatever you want 
     // here you know what's the invoked function 
     // it's void foo(int) 
     // I'm returning its type to static_assert it 
     return &foo; 
    } 
}; 

int main() { 
    float f = .42; 
    static_assert(find::which(0) == &static_cast<void(&)(int)>(foo), "!"); 
    static_assert(find::which("hello") == &static_cast<void(&)(const char *)>(foo), "!"); 
    static_assert(find::which(f) == &static_cast<void(&)(float&)>(foo), "!"); 
    static_assert(find::which(.42) == &static_cast<void(&)(double&&)>(foo), "!"); 
} 

I Ich werde diese Antwort nach einer kurzen Zeit löschen, während derer ich erwarte, dass Experten mich verfluchen. :-)

+0

Wenn Sie nur alle Überladungen aufzählen wollen, brauchen Sie 'choice <>' nicht. Sie können einfach alle Überladungen die Signatur [Demo] zurückgeben lassen (http://coliru.stacked-crooked.com/a/49779a83d15d0e36) – Barry

+0

Eigentlich ist das Ziel, Prioritäten zu geben, die alle Funktionen nicht templated (als 'void foo (int); '), dann zu den Vorlagen. Auf diese Weise listen Sie zwei Spezialisierungen auf und alle verfügbaren Funktionen, die nicht auf dem Template basieren (nicht nur das "int" Ihres Beispiels). Sicherlich kann es mit weniger Code gemacht werden, nicht mit dem, den du sowieso verlinkt hast. – skypjack

+0

Ich lese einen Kommentar von dir zu einer anderen Antwort (* es wird nicht funktionieren, wenn ich es in 'void foo (char)' *) ändere.Aus diesem Grund dachte ich, dass das Ziel darin bestünde, die nicht vorlagengestützte Funktion zu erhalten, wenn eine solche existiert, ansonsten die richtige Spezialisierung. Die 'Wahl' hilft einfach, über sie zu iterieren. Es hätte vielleicht mit einer Art "Int"/"Char" Dispatching gemacht werden können, oder was auch immer. Wie auch immer, ich hoffe, es ist mir gelungen zu erklären, was das Ziel war. – skypjack

1

Barry, Entschuldigung für das Missverständnis in meiner ersten Antwort. Am Anfang habe ich deine Frage falsch verstanden. "T. C." Es ist richtig, dass es nicht möglich ist, außer in seltenen Fällen, wenn Ihre Funktionen abhängig von den gegebenen Argumenten unterschiedliche Ergebnistypen haben. In solchen Fällen können Sie sogar die Zeiger der Funktionen erhalten.

#include <string> 
#include <vector> 
#include <iostream> 

//template <class T> T foo(T) { std::cout << "template" << std::endl; return {}; }; 
std::string foo(std::string) { std::cout << "string" << std::endl; return {}; }; 
std::vector<int> foo(std::vector<int>) { std::cout << "vector<int>" << std::endl; return {}; }; 
char foo(char) { std::cout << "char" << std::endl; return {}; }; 

template<typename T> 
struct Temp 
{ 
    using type = T (*) (T); 
}; 

#define GET_OVERLOAD(func,param) static_cast<Temp<decltype(foo(param))>::type>(func); 

int main(void) 
{ 
    auto fPtr1 = GET_OVERLOAD(foo, 0); 
    fPtr1({}); 

    auto fPtr2 = GET_OVERLOAD(foo, std::string{"hello"}); 
    fPtr2({}); 

    auto fPtr3 = GET_OVERLOAD(foo, std::initializer_list<char>{}); 
    fPtr3({}); 

    auto fPtr4 = GET_OVERLOAD(foo, std::vector<int>{}); 
    fPtr4({}); 

    auto fPtr5 = GET_OVERLOAD(foo, std::initializer_list<int>{}); 
    fPtr5({}); 

    return 0; 
} 

Die Ausgabe lautet:

char 
string 
string 
vector<int> 
vector<int>