2016-07-14 11 views
3

Ich implementiere zur Zeit eine Dataset-Helper-Klassenvorlage, die Fließkommawerte (Scalar) in einer Eigen::Matrix dynamischen Struktur aus einem Vektor von verschiedene Werttypen (Element), die zusätzlich einen Verweis auf diesen Eingabevektor speichern. Jetzt möchte ich den Konstruktor im Vektorwerttyp teilweise spezialisieren, der eine Vorlage im Skalartyp bleibt, der explizit instanziiert wird.C++, partielle Spezialisierung der 2-Argumente-Klasse Vorlage: Funktionserkennung kann nicht mit einer vorhandenen Deklaration übereinstimmen

Leider so einfach „nicht in der Lage zu Funktionsdefinition zu einer vorhandenen Deklaration überein:“ Ich bin immer auf VS 2010. Der Code ist als:

template <class Scalar, class Element> struct DataSet 
{ 
    DataSet(std::vector<Element> const & source); 

    // several generic member functions here ... 

    Eigen::Matrix<Scalar, ... > data; 
    std::vector<Element> const & source; 
}; 

template<class Scalar> 
DataSet<Scalar, SomeClass>::DataSet(std::vector<SomeClass> const & input) 
{ 
    // special impl for Element==SomeClass ... 
} 

Someclass vom Compiler automatisch herausgefunden werden soll, wenn richtig gemacht, aber ich versuchte, alle sinnvollen Kombinationen, aber noch immer:

*.cpp(79) C2244 : unable to match function definition to an existing declaration 
see declaration of 'DataSet<Scalar, Element>::DataSet' 

ich nicht in der Lage war noch durch die Suche im Internet ein passendes Beispiel zu finden. Danke im Voraus!

EDIT:

Um es präziser zu machen, die ich in meinem realen Welt Fall, dass mehrere Teil Spezialisierungen an den Konstruktor mit verschiedenen Typen für Element zB definieren können:

template<Scalar> 
DataSet<Scalar, FirstClass>::DataSet(std::vector<FirstClass> const & first) 
: data() 
, source(first) 
{ 
    // special impl here ... 
} 

template<Scalar> 
DataSet<Scalar, std::shared_ptr<SecondClass> >::DataSet(std::vector<std::shared_ptr<SecondClass> > const & second) 
: data() 
, source(second) 
{ 
    // special impl here ... 
} 

Es ist nicht erwünscht, die Klasse vollständig auf einen bestimmten Typennamen zu redefinieren/zu spezialisieren. Dann nützt es wenig, eine Vorlage zu sein. Ich möchte die Lösung so wie sie ist, sonst könnte es andere Strategien für mein Problem geben.

FIN:

Da es sieht aus wie nicht möglich ist, nur spezialisiert den Konstruktor den Typ Element zwischen Klassenvorlage und Konstruktor zu teilen (was irgendwie zu einer impliziten Spezialisierung der Klasse verwandt ist) i entfernt die Referenz source aus der Klassenvorlage vollständig und kopiert die benötigten Informationen in einen generischen Container und implementiert die Konstruktoren über Überladungen.

Antwort

3

Nach §14.7.3.16:

In einer expliziten Spezialisierung Erklärung für ein Mitglied einer Klasse-Vorlage oder ein Mitglied Vorlage, die in Namespacebereich angezeigt wird, das Element Template und einige seiner umgebenden Klasse Vorlagen können bleiben unspezialisiert, mit der Ausnahme, dass die Deklaration eine Klassenmitgliedsvorlage nicht explizit spezialisieren soll, wenn ihre einschließenden Klassenvorlagen auch nicht explizit spezialisiert sind.

Dennoch können Sie std :: enable_if auf Teil spezialisieren Ihre contructor verwenden:

template <class Scalar, class Element> struct DataSet 
{ 
    template <class T> 
    DataSet(std::vector<T> const & input, std::enable_if_t<!std::is_same<T, SomeClass>{}> * = nullptr) { 
     std::cout << "Element\n"; 
    } 
    template <class T> 
    DataSet(std::vector<T> const & input, std::enable_if_t<std::is_same<T, SomeClass>{}> * = nullptr) { 
     std::cout << "SomeClass\n"; 
    } 
}; 

Aber auf diese Weise ist restriktiv:

  • alle Bedingungen müssen exclusives sein
  • Sie müssen den Code Ihrer Klasse für jede neue Klasse ändern, die Sie behandeln möchten.

Stattdessen würde ich Ihnen raten, eine Vorlage Helfer Struktur zu verwenden:

DataSet(std::vector<Element> const & input) { 
    Helper<Element>::do_it(input); 
} 

, die Sie können, wie Sie spezialisieren wollen:

template <class Element> 
struct Helper { 
    static void do_it(std::vector<Element> const & input) { 
     std::cout << "General form with Element\n"; 
    } 
}; 


template<> 
struct Helper<SomeClass> { 
    static void do_it(std::vector<SomeClass> const & input) { 
     std::cout << "SomeClass\n"; 
    } 
}; 

template<> 
struct Helper<SomeOtherClass> { 
    static void do_it(std::vector<SomeOtherClass> const & input) { 
     std::cout << "SomeOtherClass\n"; 
    } 
}; 

... 
+1

Vielen Dank für Ihre Hilfe, aber dies wird nicht den Verweis auf die Quelldaten direkt möglich machen und erhöht Codezeilen im Gegensatz zu meinem ursprünglichen Wunsch, es so einfach wie möglich zu halten. Ich habe diesen Verweis jetzt auf die Quelldaten entfernt und die Informationen zu einem generischen Container zusammengefasst. Die Konstruktoren können jetzt als Überladungen ohne 'Element' implementiert werden. Aber ich bin immer noch Kuriositäten, wo das Problem liegt, weil im Allgemeinen der Weg begonnen wird, sollte der Weg sein, von einem syntaktischen Gesichtspunkt zu gehen. – daku

+0

@daku Es ist etwas intuitiv anzunehmen, dass spezialisierte Vorlagen Mitglieder von der primären "Basis" erben werden, aber das ist einfach nicht der Fall. Dies ist darauf zurückzuführen, dass Vorlagenargumente bedeuten könnten, dass bestimmte Elemente in bestimmten Spezialisierungen vollständig nicht anwendbar sind, wodurch eine implizite "Vererbung" problematisch wird. Wenn dies der Fall ist, muss entschieden werden, dass eine vollständige Neudeklaration erforderlich ist und es nicht lohnenswert ist, eine Menge arkaner Syntax hinzuzufügen, um anzugeben, ob Mitglieder "geerbt" werden sollen oder nicht, wenn Sie _actual_vererbung einfach verwenden können, um sicherzustellen, dass dies der Fall ist. Aber ich werde sehen, ob ich eine Grundannahme finden kann –

+0

@underscore_d: Das liegt daran, dass Spezialisierung keine Vererbung ist ;-) Die Spezialisierung der Klassenvorlage macht eine komplett neue Klasse. Mein Ziel war es, den Konstruktor zu spezialisieren, was offensichtlich nicht möglich ist, weil 'Element' ein Template-Argument für die Klassenvorlage ist. Es sollte einfach möglich sein, wenn ich den Konstruktor zu einer Template-Funktion mache, aber dann ist es wahrscheinlich nicht möglich, das Argument 'typename Element' zwischen Klassen- und Funktions-Template (für den ctor, oder ist es irgendwie?) Zu teilen. – daku

3

Bei der Definition Ihres Konstruktors haben Sie nicht beide Vorlagenargumente für die Klasse angegeben. Das müßte überarbeitet werden, wie folgt:

template<typename T_Scalar, typename T_Element> 
DataSet<T_Scalar, T_Element>     // template args for type 
::DataSet(std::vector<T_Element> const &input) // but none for constructor 
{ 
    // stuff 
} 

Tangential Zusammenhang: Im Gegensatz zu Methoden, template arguments for classes cannot be deduced from constructor calls. Das ist: until C++17 comes around! umwerben!

Der nächste Stolperstein, dem Sie gegenüberstanden, ist, dass Vorlagenspezialisierungen Mitglieder nicht von ihrer primären Vorlage "erben". Es ist etwas intuitiv anzunehmen, dass es so wäre, aber es ist einfach nicht so. Bis ich eine offizielle Begründung finde, I nehme an, dass es ist, weil Schablonenargumente bestimmte Mitglieder für eine Spezialisierung völlig unzutreffend machen können, die implizite 'Vererbung' problematisch machen. Wenn dies der Fall wäre, wäre entschieden worden, eine vollständige Neuformulierung zu verlangen/nicht lohnend, eine obskure Syntax hinzuzufügen, um anzugeben, welche primären Basismitglieder "vererbt" werden, wenn Sie einfach echte Vererbung verwenden können, um sicherzustellen, dass dies der Fall ist.

Wie auch immer, das bedeutet, dass Sie, um eine partielle Spezialisierung zu erhalten, die ganze Sache - in diesem Fall die Klasse und ihren Konstruktor - deklarieren müssen, bevor Sie die Definition dieses Konstruktors spezialisieren können. Sie hatten dies nicht im Voraus angekündigt, daher beschwerte sich der Compiler zu Recht darüber, dass er keine Erklärung sehen konnte.

// Define specialised class 
template<typename T_Scalar> 
class DataSet<T_Scalar, SomeClass> 
{ 
public: 
    // Declare its ctor 
    DataSet(std::vector<SomeClass> const &); 
} 

// Implement its ctor 
template<typename T_Scalar> 
DataSet<T_Scalar, SomeClass> // complete template args 
::DataSet(std::vector<SomeClass> const &input) 
{ 
    // stuff 
} 

See my working example of an equivalent template class, showing general vs. specialised instantiations.

zu Ihrer ursprünglichen Verwirrung hinzuzufügen, die fair ist! - Beachten Sie, dass Out-of-Line-Definitionen in der Tat sehr kompliziert werden können, wenn eine Schablonenklasse selbst eine Schablonenfunktion enthält, weil dann Klauseln, z.

template<typename TA, typename TB> 
class Widget { 
    template<typename TC> 
    void accept_gadget(TC &&gadget); 
}; 

template<typename TA, typename TB> 
template<typename TC> 
Widget<TA, TB> 
::accept_gadget(TC &&gadget) 
{ 
    /* ... */ 
} 

Etwas, das viel in vielen Zusammenhängen, vor allem auch solche out-of-line Vorlagendefinitionen helfen wird, ist, wenn the proposal to allow namespace class in einer zukünftigen Version akzeptiert wird. Sehr traurig, dass es nicht in C++ 17 geschafft hat ... und sehr merkwürdig, dass es auf der ersten Stelle überhaupt fehlte!

+0

Ok, ich war irgendwie in Angst, dass Ich erwarte etwas, das noch nicht für C++ - Vorlagen unterstützt wird (da ich keine passenden Beispiele gefunden habe), aber ich sollte es irgendwie schaffen können, nicht wahr? Ich werde wahrscheinlich später einen Verweis auf den Eingabevektor benötigen, deshalb sollte 'Element' ein Template-Argument für die Klasse bleiben, nicht der Konstruktor ... irgendwelche Vorschläge? – daku

+0

@daku Ja, es ist ein Template-Argument, also nimm es einfach als eins! siehe meine Edit –

+0

War nicht der Zweck, eine teilweise Spezialisierung mit "Element" = "SomeClass" von der allgemeinen Form zu unterscheiden? –

Verwandte Themen