2016-07-29 11 views
2

Ich habe eine Vorlage struct SFoo, die ein Mitglied struct enthält SZug:C++: Teilweise spezialisiert Vorlage des Typ-Parameter als ein anderes Mitglied-Typ der Template-Klasse

template <typename tTYPE> 
struct SFoo 
    { 
    struct SZug {}; 
    }; 

ich eine andere Struktur haben SBar, die einen Typ Parameter nimmt:

template <typename tTYPE> 
struct SBar 
    { /* stuff */ }; 

ich möchte SBar mit SZug für den Typ-Parameter spezialisieren, etwa so:

template <typename tTYPE> 
struct SBar<typename SFoo<tTYPE>::SZug> 
    { /* different stuff */ }; 

Dies lässt sich nicht kompilieren - LLVM Ausgänge:

nicht ableitbar Template-Parameter 'TTYPE'

Während ein Compiler dies leicht, wenn es gewünscht ableiten könnte, ich vermute, es ist nur dass die C++ - Spezifikation diesen Fall spezifisch abdecken müsste.

Gibt es eine Möglichkeit, dies zu erreichen?
. (Anmerkung: Im Moment arbeite ich um es von SZug außerhalb von SFoo bewegt und eine using Meldung ab, aber es ist hässlich)

+0

Was haben Sie danach vor? Es ist mir nicht klar, was Sie von der "Spezialisierung" erwarten. Können Sie uns zeigen, wie Sie eine Variable des nichtspezialisierten und spezialisierten 'SBar' deklarieren würden? – Holt

+0

Ich verwende diese rein als Merkmalstypen, so dass sie nie tatsächlich instanziiert werden. Bar bietet einfach einen consExpr Mitgliedszeiger für eine andere (nicht erwähnte) Klasse (plus ein paar andere Dinge). Normalerweise muss der Member-Pointer explizit angegeben werden, aber für die 'SZug'-Spezialisierung kann er bestimmt werden. - (Letztendlich hat dies alles damit zu tun, generische eingebettete N-zu-N-Container aus 1-zu-N-generischen Containern zu generieren - der Member-Pointer zeigt auf die Container- oder Knoten-Information "embedded" (dh eine Member-Variable) im Benutzer Art). – xaxazak

+0

I Sie können leicht 'SZug' geändert werden, um es zu erkennen, es ist ziemlich einfach zu tun, was Sie wollen, mit SFINAE (siehe meine Antwort). Wenn Sie nicht können, bin ich nicht sicher, ob es eine Möglichkeit gibt zu erkennen, dass "tTYPE" die "SFoo :: SZug" -Klasse ist. – Holt

Antwort

2

Ich bin nicht sicher, ob ich voll und ganz verstanden, was Sie tun wollen, aber Sie könnten versuchen, das folgende (es erfordert nur ein bestimmtes Attribut zu SZug Zugabe:

template <typename tTYPE> 
struct SFoo { 
    struct SZug { 
     // Add this to be able to obtain SFoo<T> from SFoo<T>::SZug 
     using type = tTYPE; 
    }; 
}; 

Dann eine kleine Vorlage, wenn ein Typ zu überprüfen ist ein SFoo<T>::SZug:

template <typename tTYPE, typename Enabler = void> 
struct is_SZug: public std::false_type { }; 

template <typename tTYPE> 
struct is_SZug<tTYPE, typename std::enable_if< 
    std::is_same<tTYPE, typename SFoo<typename tTYPE::type>::SZug>{} 
>::type>: public std::true_type { }; 

Und eine leichte Modifikation der SBar Vorlage, um die „Spezialisierung“ zu aktivieren, wenn der Typ ein SZug ist:

template <typename tTYPE, typename Enabler = void> 
struct SBar 
    { static void g(); }; 

template <typename tTYPE> 
struct SBar<tTYPE, typename std::enable_if<is_SZug<tTYPE>{}>::type> 
    { static void f(); }; 

Ein wenig Kontrolle:

void f() { 
    SBar<int>::g(); 
    SBar<SFoo<int>::SZug>::f(); 
} 

Hinweis: Sie können auch festlegen direkt SFoo<T> als type Attribut in SFoo<T>::SZug, würden Sie einfach das zweite Argument von std::is_same etwas ändern müssen.

+0

Ja, das löst das Problem, Tyvm. Für mein aktuelles Problem ist das Hinzufügen von 'using type = tTYPE;' ziemlich einfach. Der zusätzliche Parameter auf 'SBar' ist nerviger, aber immer noch machbar. Ich überlege gerade, ob das aufgeräumter ist als meine jetzige Lösung. - Wenn morgen keine bessere Antwort kommt, werde ich das als gelöst markieren. Prost. – xaxazak

+0

@xaxazak Es ist ein häufig verwendetes Idiom, um solchen 'Enabler'-Parameter zu Templates hinzuzufügen (es sollte die meiste Zeit automatisch abgeleitet werden), aber es kann ein Problem sein, wenn Sie 'SBar' als Template-Template-Argument verwenden müssen. Vielleicht, wenn Sie näher erläutern könnten, warum es nervig ist, und ich könnte Ihnen vielleicht eine geeignetere Lösung vorschlagen? – Holt

+0

Meistens ist es Modularität. SBar wird bereits häufig (in 1-zu-N-Containern) verwendet und sollte dieses neue Feature (N-zu-N-Container) nicht kennen. Aber es ist alles mein eigener Code, damit ich ihn aktualisieren kann, und er wird nirgends in Template-Vorlagen verwendet (obwohl 'SZug' einen Template-Template-Parameter verwendet). – xaxazak

1

können Sie erhalten die Effekt, für die Sie die folgende gesuchte durch (die ausdruckt 0 1, BTW):

#include <type_traits> 
#include <iostream> 

namespace detail 
{ 
    struct SZugBase{}; 
} 

template <typename tTYPE> 
struct SFoo                                 
{ 
    struct SZug : public detail::SZugBase {}; 
}; 

template<typename tType, bool IsFoo> 
struct SBarBase 
{ 
    int value = 0; 
}; 

template<typename tType> 
struct SBarBase<tType, true> 
{ 
    int value = 1; 
}; 

template <typename tTYPE> 
struct SBar : public SBarBase<tTYPE, std::is_convertible<tTYPE, detail::SZugBase>::value> 
{ /* stuff */ }; 

int main() 
{ 
    SBar<int> b0; 
    SBar<SFoo<int>::SZug> b1; 

    std::cout << b0.value << " " << b1.value << std::endl; 
} 

Erklärung

Zuerst wir SZug geben eine reguläre Klasse b ase:

namespace detail 
{ 
    struct SZugBase{}; 
} 

template <typename tTYPE> 
struct SFoo            
{ 
    struct SZug : public detail::SZugBase {}; 
}; 

Beachten Sie Folgendes:

  1. SZugBase ist durch nichts parametriert, so ist es leicht, sich darauf zu beziehen, unabhängig von der Parameter von SFoo

  2. SZugBase ist in ein detail Namespace, also sagen Sie nach gängigen C++ - Konventionen, dass Clients Ihren Code ignorieren.

Jetzt geben wir SBar zwei Basisklassen, spezialisiert auf, ob etwas umwandelbar in die Nicht-Template Basis von SZug:

template<typename tType, bool IsFoo> 
struct SBarBase 
{ 
    int value = 0; 
}; 

template<typename tType> 
struct SBarBase<tType, true> 
{ 
    int value = 1; 
}; 

Schließlich brauchen wir nur SBar eine Unterklasse dieser Basen zu machen (abhängig von der Spezialisierung):

template <typename tTYPE> 
struct SBar : public SBarBase<tTYPE, std::is_convertible<tTYPE, detail::SZugBase>::value> 
{ /* stuff */ }; 

Beachten Sie, dass Sie nicht SBar Sie spezialisieren sich ihr E, spezialisieren Sie eher die Basisklassen. Dies hat jedoch den gleichen Effekt.

+0

Das funktioniert für 'SBar > b1;'. Ich suche leider nach SBar :: '**' SZug' ** '> b1;' (welches AFAICT würde immer noch 0 drucken). – xaxazak

+0

@xaxazak Sie haben Recht - ich habe das Detail in der Frage verpasst, aber das Prinzip gilt. Ein paar kleine Änderungen in den Details der Antwort machten dies für Ihre ursprüngliche Absicht gültig. Die wichtigsten Punkte sind: 1. Eine Nicht-Template- "Detail" -Basis und 2. Spezialisieren einer Basisklasse basierend darauf, ob sie in diese Basis konvertierbar ist. –

+0

Ja, das behebt das Problem. Ich drücke mir die Daumen, vielleicht gibt es einen besseren Weg. Sowohl Ihre Lösungen als auch Holts könnten akzeptiert werden, aber meiner Meinung nach ist das Hinzufügen eines Fixer-Member-Typs (Holts Methode) sehr viel netter als das Erben von einer Fixer-Struktur. – xaxazak