2015-01-30 8 views
7

Könnte ich SFINAE (oder eine andere Technik) für using Deklaration verwenden, während privat von Template-Klasse abgeleitet? siehe Zum besseren Verständnis Code unten:'using' Deklaration als SFINAE

#include <iostream> 

struct S1 { 
    void f() { std::cout << "S1::f\n"; } 
}; 

struct S2 { 
    void f() { std::cout << "S2::f\n"; } 
    void g() { std::cout << "S2::g\n"; } 
}; 

template <class T> 
struct D : private T { 
    using T::f; 
    // using T::g; // need this only if T provides g() function 
}; 

int main() { 
    D<S1>().f(); // ok. Prints 'S1::f' 
    D<S2>().f(); // ok. Prints 'S2::f' 
    D<S2>().g(); // fail. But wants to be ok and prints 'S2::g' 
    return 0; 
} 

Wie kann ich die gewünschte Verhalten zu erreichen (wenn es möglich ist)?

+0

möglich Duplikat von [Ist es möglich, eine C++ Vorlage zu schreiben, um nach dem Vorhandensein einer Funktion zu suchen?] (Http://stackoverflow.com/questions/257288/is-it-possible-to-write-ac-template- zu-check-for-a-functions-Existenz) – Klaus

+2

@Klaus Nein, das ist kein Duplikat. Diese Frage kann leicht annehmen, dass wir bereits das Merkmal 'has_function_named_g ' haben. Was jetzt? Wie wenden Sie es auf die 'using' Deklaration an? – Angew

Antwort

4

C++ Teil Vorlage Spezialisierung und verwenden decltype(void(&T::g)) für SFINAE

#include <iostream> 
#include <type_traits> 

struct S1 { 
    void f() { std::cout << "S1::f\n"; } 
}; 

struct S2 { 
    void f() { std::cout << "S2::f\n"; } 
    void g() { std::cout << "S2::g\n"; } 
}; 

template <class T, class V = void> 
struct D : private T { 
    using T::f; 
}; 

template <class T> 
struct D<T, decltype(void(&T::g))> : private T { 
    using T::f; 
    using T::g; // need this only if T provides g() function 
}; 

int main() { 
    D<S1>().f(); // ok. Prints 'S1::f' 
    D<S2>().f(); // ok. Prints 'S2::f' 
    D<S2>().g(); // ok. Prints 'S2::g' 
    return 0; 
} 

Live Demo


Edit:

Dies ist ein weiterer Ansatz, der flexibler ist, aber ich habe keine Ahnung, wie funktioniert private virtual Vererbung funktioniert mit echten Anwendungsfällen. Bitte lassen Sie mich wissen, wenn es ein Problem verursachen kann (z. B. UB).

#include <iostream> 
#include <type_traits> 

struct S1 { 
    void f() { std::cout << "S1::f\n"; } 
}; 

struct S2 { 
    void f() { std::cout << "S2::f\n"; } 
    void g() { std::cout << "S2::g\n"; } 
}; 

struct S3 { 
    void g() { std::cout << "S3::g\n"; } 
}; 

template <class T, class = void> 
struct D_f {}; 

template <class T> 
struct D_f<T, decltype(void(&T::f))> : private virtual T { 
    using T::f; 
}; 

template <class T, class = void> 
struct D_g {}; 

template <class T> 
struct D_g<T, decltype(void(&T::g))> : private virtual T { 
    using T::g; 
}; 

template <class T> 
struct D : D_f<T>, D_g<T> { 
}; 


int main() { 
    D<S1>().f(); 
    D<S2>().f(); 
    D<S2>().g(); 
    D<S3>().g(); 
    return 0; 
} 

Live Demo

+1

Dies skaliert jedoch nicht gut. Für n Funktionen, die verfügbar sein können oder nicht, müssen Sie 2 ** n Klassendefinitionen definieren. – hvd

+0

@BryanChen Ist es möglich, die Änderung innerhalb der Haupt-D-Klassenvorlage zu begrenzen, dh ohne Spezialisierung und Duplizierung von Code wie 'using T :: f;'? – alexolut

5

Eine Variante von Bryan Chens Antwort, die hässlichen, sieht aber macht es einfacher, mehrere Kontrollen zu verlängern, und erfordern nicht den Code zu duplizieren, die zwischen D<type-with-f> und D<type-without-f> zu verwenden, ist gemeinsam genutzt werden eine Vererbungskette, bei der jeder Schritt ein zusätzliches Mitglied überprüft. Die einzige erforderliche Duplizierung ist gegebenenfalls die Vererbung von Konstruktoren.

struct A { 
    void f() { } 
    void g() { } 
    void i() { } 
}; 

// The generic case. D<T, char[N]> simply provides what D<T, char[N+1]> provides. 
template <typename T, typename U = char[1]> 
struct D : D<T, char[sizeof(U) + 1]> { 
    using D<T, char[sizeof(U) + 1]>::D; 
}; 

// The end of the chain. This is where T gets inherited. It declares all of its own 
// specialisations as its friends, so that they can access other members of T. 
template <typename T> 
struct D<T, char[6]> : private T { 
    template <typename, typename> 
    friend struct D; 

    D(int) { } 
    void fun() { } 
}; 

// Check for T::f. 
template <typename T> 
struct D<T, char[2 + !sizeof(&T::f)]> : D<T, char[3]> { 
    using D<T, char[3]>::D; 
    using T::f; 
}; 

// Check for T::g. 
template <typename T> 
struct D<T, char[3 + !sizeof(&T::g)]> : D<T, char[4]> { 
    using D<T, char[4]>::D; 
    using T::g; 
}; 

// Check for T::h. 
template <typename T> 
struct D<T, char[4 + !sizeof(&T::h)]> : D<T, char[5]> { 
    using D<T, char[5]>::D; 
    using T::h; 
}; 

// Check for T::i. 
template <typename T> 
struct D<T, char[5 + !sizeof(&T::i)]> : D<T, char[6]> { 
    using D<T, char[6]>::D; 
    using T::i; 
}; 

int main() { 
    D<A> d = 4; // ok: verify that constructors got inherited 
    // A &a = d; // error: verify that inheritance of A is private 
    d.f(); // ok: verify that f got inherited 
    d.g(); // ok: verify that g got inherited 
    // d.h(); // error: verify that h is not available 
    d.i(); // ok: verify that i got inherited 
    d.fun(); // ok: verify that the inheritance chain didn't get broken 
} 

Hinweis: Statt &T::f zu prüfen, können Sie etwas mit std::declval<T>().f() stattdessen tun wollen. Ersteres kann mit überladenen Funktionen nicht umgehen.

+0

Hmm, ich hätte 'std :: integral_constant' benutzen können, aber sehr nett. –

+0

@ T.C. Heh, das ist eines der Dinge, von denen ich weiß, dass sie da sind, aber irgendwie nie enden, weil, wann immer es nützlich sein könnte, habe ich bereits eine Lösung in meinem Kopf gefunden, die es nicht braucht. :) – hvd