2017-02-27 5 views
1

Ich möchte einen MP Prädiktor für einen Typ und eine Funktion schreiben. Es scheint, dass etwas illegal ist:Typ und Nicht-Typ Vorlage Spezialisierung

#include <iostream> 

template <class R> 
struct X { 
    static constexpr int value = 0; 
}; 

// Specialization for type 
template <> 
struct X<int(int)> { 
    static constexpr int value = 1; 
}; 

// ERROR: Redefinition with diffrent kind 
template <int (*F)(int)> 
struct X { 
    static constexpr int value = 2; 
}; 

int fun(int); 

int main(int, char* []) 
{ 
    std::cout << "X<int>: " << X<int>::value << '\n'; 
    std::cout << "X<int(int)>: " << X<int(int)>::value << '\n'; 
    std::cout << "X<decltype(fun)>: " << X<decltype(fun)>::value << '\n'; 
    std::cout << "X<fun>: " << X<fun>::value << '\n'; 
    return 0; 
} 

Ist es möglich, so etwas zu erreichen? Weitere Details: Wofür?

  1. Um metaprogramming
  2. lernen, um einen universellen Prädiktor zu schreiben, die eine Funktion/eine Objektinstanz mit einem bestimmten Argument (so etwas wie is_callable von C++ 17)
+2

Sie könnten [dies] (http://coliru.stacked-crooked.com/a/97298936e850a900) interessant finden. Oder nicht. – ildjarn

+0

Sicherlich etwas verwandt und interessant, auch wenn das Problem nicht gelöst wird. Also danke :) – user2146414

Antwort

0

Ich denke, wenn rufen Sie sagen kann es ist nicht möglich. Das Template-Argument kann nur ein Typ oder ein Wert sein. In der Deklaration:

template <class R> 
struct X { 
    static constexpr int value = 0; 
}; 

Sie angeben X, um einen Typ als Argument zu nehmen.

// ERROR: Redefinition with diffrent kind 
template <int (*F)(int)> 
struct X { 
    static constexpr int value = 2; 
}; 

Sie möchten X spezialisieren, um einen Wert (einen Funktionszeiger) zu nehmen. Das kollidiert mit der ersten Deklaration von X. Daher erhalten Sie den Neudefinitionsfehler.

Wenn Sie Ihren Prädiktor auf eine allgemeinere Weise implementieren möchten, müssen Sie das SFINAE-Muster verwenden. Ich habe etwas ähnliches in meiner Vergangenheit geschrieben, aber aufgegeben, weil es zu ungeschickt zu benutzen und fehleranfällig ist. Ich kann mich nicht mehr genau erinnern, aber ich glaube, ich habe einige Fälle gefunden, in denen es nicht funktioniert hat. Für pädagogische Zwecke schreibe ich den Code hier:

#include <type_traits> 
#include <iostream> 

namespace Detail { 

template < 
    class Function, 
    class Result, 
    class Enable, 
    class... Args 
> 
struct isFunctionImpl { 
    static constexpr bool value = false; 
}; 

template < 
    class Function, 
    class Result, 
    class... Arg 
> 
struct isFunctionImpl < 
    Function, 
    Result, 
    typename std::enable_if< 
     std::is_same< 
      Result, 
      decltype(std::declval<Function>()(*reinterpret_cast<Arg*>(0)...)) 
     >::value 
    >::type, 
    Arg... 
> { 
    static constexpr bool value = true; 
}; 

} // End of namespace Detail 

template < 
    class Function, 
    class Result, 
    class... Args 
> 
struct isFunction { 
    static constexpr bool value = Detail::isFunctionImpl<Function, Result, void, Args...>::value; 
}; 

int foo(int bar) { 
    return 0; 
} 

int main() { 
    std::cout << "Is function " << isFunction<decltype(foo), int, int>::value << std::endl; 
    std::cout << "Is function " << isFunction<decltype(foo), int, std::string>::value << std::endl; 
} 
2

Ist es möglich, so etwas zu erreichen?

Was Sie effektiv fragen ist, wenn Sie Klassenvorlagen überladen können. Nein, du kannst nicht.

Sie können jedoch Funktionsschablonen sicherlich überladen - Sie können eine Funktionsvorlage haben, die einen nicht abgeleiteten Vorlagentyp-Parameter verwendet, ihn spezialisieren und dann eine andere Funktionsschablone verwenden, die einen nicht abgeleiteten Vorlagentyp nicht-Typ-Parameter verwendet:

#include <iostream> 

template <class R> constexpr int X() { return 0; }  

// specialization for type 
template <> constexpr int X<int(int)>() { return 1; } 

// Redefinition with different kind 
template <int (*F)(int)> 
constexpr int X() { return 2; } 

int fun(int); 

int main(int, char* []) 
{ 
    std::cout << "X<int>: " << X<int>() << std::endl; 
    std::cout << "X<int(int)>: " << X<int(int)>() << std::endl; 
    std::cout << "X<decltype(fun)>: " << X<decltype(fun)>() << std::endl; 
    std::cout << "X<fun>: " << X<fun>() << std::endl; 
    return 0; 
} 

Das druckt 0, 1, 1 und 2, wie gewünscht.

+0

Und mit 'constexpr' kann man sogar die Ausgabe der Funktion als Template-Parameter verwenden. – Omnifarious

Verwandte Themen