2017-12-24 12 views
1

Ich versuche, einige Funktionen mit Template-Spezialisierung für Eigen-Typen zu schreiben, im Anschluss an diese Frage: Eigen: type deduction in template specialization of base-classVorlage Spezialisierung Funktion mit Eigen Argumente

ich dies schrieb:

#include <type_traits> 
#include <Eigen/Core> 

namespace isEigenPlainObjectBaseDetail { 
    template <typename T> 
    std::true_type test(const Eigen::PlainObjectBase<T>); 
    std::false_type test(...); 
} 
template <typename T> 
struct isEigenPlainObjectBase : 
     public decltype(isEigenPlainObjectBaseDetail::test(std::declval<T>())) {}; 

template <typename T, typename Enable = void> 
void foo(T& obj) { 
    std::cout << "Generic Called!\n"; 
} 

template <typename T, typename std::enable_if<isEigenPlainObjectBase<T>::value>::type> 
void foo(T& obj) { 
    std::cout << "Eigen Specialization Called!"; 
} 

int main() { 
    Eigen::MatrixXd m; 
    Eigen::VectorXd v; 
    int i = 0; 
    foo(i); 
    foo(m); 
    foo(v); 
    return 0; 
} 

aber dies erfordert jedes Mal die generische Funktion :

Generic Called! 
Generic Called! 
Generic Called! 

Was mache ich falsch? Wie kann ich Template-Spezialisierungen für Funktionen mit Eigenmatrizen und Vektoren schreiben?

Antwort

0

nicht sicher, dass dies das einzige Problem (kein Eigen installiert, so kann ich nicht überprüfen, sorry), aber ...

Sie können teilweise mit Spezialisierung structs und Klassen, keine Funktionen.

Also dieser Code

template <typename T, typename Enable = void> 
void foo(T& obj) { 
    std::cout << "Generic Called!\n"; 
} 

template <typename T, typename std::enable_if<isEigenPlainObjectBase<T>::value>::type> 
void foo(T& obj) { 
    std::cout << "Eigen Specialization Called!"; 
} 

nicht funktioniert und ich bin, dass die Kompilierung überrascht.

EDIT: wie von Quentin (danke!) Der zweite Template-Parameter der zweiten ist ein Nicht-Typ; Es gibt also keine Kollision zwischen den beiden foo(), aber die zweite wird jemals ausgeschlossen, weil der nicht-type Template-Parameter vom Typ void ist. Und das ist nicht erlaubt.

Um SFINAE zu verwenden, sollten Sie eine class oder struct; so etwas wie

template <typename T, typename = void> 
struct foo 
{ 
    static void func (T & obj) 
    { std::cout << "Generic Called!\n"; } 
}; 

template <typename T 
struct foo<T, typename std::enable_if<isEigenPlainObjectBase<T>::value>::type> 
{ 
    static void func (T & obj) 
    { std::cout << "Eigen Specialization Called!"; } 
}; 

und kann als

foo<decltype(i)>::func(i); 
foo<decltype(m)>::func(m); 
foo<decltype(v)>::func(v); 

Vielleicht können Sie eine bar() Vorlage Hilfsfunktion

template <typename T> 
void bar (T & t) 
{ foo<T>::func(t); } 

und einfach

bar(i); 
bar(m); 
bar(v); 

definieren verwendet werden rufen Oder vielleicht ist es einfach Verwenden Sie Tag-Dispatching.

Wenn Sie definieren ein paar foo() einen anderen zusätzlichen Parameter

template <typename T> 
void foo (T &, std::false_type const &) 
{ std::cout << "Generic Called!\n" } 

template <typename T> 
void foo (T &, std::true_type const &) 
{ std::cout << "Eigen Specialization Called!"; } 

erhalten, sollten Sie in der Lage sein, die richtigen foo() mit einer bar() Hilfsfunktion wählen wie diese

template <typename T> 
void bar (T & t) 
{ foo(t, isEigenPlainObjectBase<T>{}); } 

und nach wie vor , einfach anrufen

bar(i); 
bar(m); 
bar(v); 

[Achtung: nicht getestet]

+1

Ich denke, dass die zweite Funktion eine Überladung ist, die gültig ist, weil der zweite Vorlagenparameter ein Nicht-Typ ist. Es wird jedoch immer SFINAE ausgegeben, weil 'std :: enable_if <> :: type' 'void' ist, was nicht erlaubt ist. – Quentin

+0

@Quentin - Ich habe nicht bemerkt, war nicht 'typename = typenname std :: enable_if' sondern nur' typenname std :: enable_if'; Antwort modifiziert um dies explizit zu machen; Vielen Dank! – max66

+0

ok, gut zu wissen! : D Ich habe es mit gcc kompiliert, habe es nicht mit msvc versucht. aber natürlich hat es nicht funktioniert! Ich hatte gehofft, in etwas in der Eigen-Bibliothek entworfen, die erlaubt, alle Eigenebenen Objektbasis mit Vorlagen zu diskriminieren! –

0

Wenn Sie einfach eine Funktion für die zwei Typen, die Sie übergeben, spezialisieren möchten, ist es wahrscheinlich viel einfacher als Sie denken.

Aktueller Code:

template <typename T, typename Enable = void> 
void foo(T& obj) { 
    std::cout << "Generic Called!\n"; 
} 

template <typename T, typename std::enable_if<isEigenPlainObjectBase<T>::value>::type> 
void foo(T& obj) { 
    std::cout << "Eigen Specialization Called!"; 
} 

Neuer Code:

template <typename T> 
void foo(T& obj); 

template <> 
void foo(Eigen::MatrixXd& obj) { 
    std::cout << "Eigen Specialization Called!" << std::endl; 
} 

template <> 
void foo(Eigen::VectorXd& obj) { 
    std::cout << "Eigen Specialization Called!" << std::endl; 
} 

template <typename T> 
void foo(T& obj) { 
    std::cout << "Generic Called!" << std::endl; 
} 

Hinweis, kompiliert nicht zu testen.

Eine andere Möglichkeit, dies zu tun, ist ein Merkmal zu Ihren interessierten Typen hinzufügen, wie können Sie das tun?

Die Art, die ich empfehlen würde, ist das folgende. (Hinweis kompiliert/nicht getestet)

template<typename T> 
struct isEigenTrait; 

//specializations for all of the eigen types you are interested in 
template<> 
struct isEigenTrait<Eigen::MatrixXd> { 
    typedef std::true_type value; 
}; 

//... 

template<typename T> 
struct isEigenTrait { 
    typedef std::false_type value; 
}; 

template<class T, std::enable_if_t<isEigenTrait<T>::value, true_type> = 0> 
void foo(T& obj) { 
    std::cout << "Eigen Specialization Called!" << std::endl; 
} 

template<class T, std::enable_if_t<isEigenTrait<T>::value, false_type> = 0> 
void foo(T& obj) { 
    std::cout << "Generic Called!" << std::endl; 
} 

-Code von C++ Referenz geändert sollte so funktionieren.

Um alle Eigenmatrizen in einem einzigen Merkmal zu erhalten, sollte das folgende funktionieren.

//specializations for all of the eigen types you are interested in 
template<typename Scalar, int RowsAtCompileTime, int ColsAtCompileTime> 
struct isEigenTrait< Eigen::Matrix<Scalar, RowsAtCompileTime, ColsAtCompileTime> > { 
    typedef std::true_type value; 
}; 

Sie müssen wahrscheinlich das gleiche für jede der Template-Typen Sie die isEigenTrait zum Hinzufügen tun wollen.

+0

Ich war schon Die Verwendung dieser Lösung führte jedoch zu viel Codeverdopplung.Ich nutze viele verschiedene Eigenmatrizen (Row major oder Column major, static oder dynamic), und für diesen Fall hoffte ich auf eine einzige Spezialisierung, um "zu fangen" "all diese Matrizen! –

+0

Wenn Sie das wirklich tun wollen, sehen Sie sich die Dokumentation zu enable_if http://en.cppreference.com/w/cpp/types/enable_if – ceorron

+0

an. Siehe obige Antwort. – ceorron

Verwandte Themen