2015-01-30 14 views
7

Potrei usare SFINAE (o un'altra tecnica) per la dichiarazione using mentre privata deriva dalla classe template? Per una migliore comprensione si veda codice qui sotto:'using' dichiarazione come 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; 
} 

Come posso raggiungere il comportamento desiderato (se possibile)?

+0

possibile duplicato di [È possibile scrivere un modello C++ per verificare l'esistenza di una funzione?] (Http://stackoverflow.com/questions/257288/is-it-possible-to-write-ac-template- to-check-for-a-functions-exist) – Klaus

+2

@Klaus No, questo NON è un duplicato. Questa domanda può facilmente assumere che abbiamo già il carattere 'has_function_named_g '. Ora cosa? Come si applica alla dichiarazione 'using'? – Angew

risposta

4

C++ modello di specializzazione parziale e utilizzare decltype(void(&T::g)) per 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:

Questo è un altro approccio che è più flessibile, ma non ho idea di come fa L'ereditarietà private virtual funziona con casi d'uso reali. Per favore fatemi sapere se potrebbe causare problemi (ad es. 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

Tuttavia, questo non si adatta bene. Per n funzioni che possono o non possono essere disponibili, è necessario specificare due definizioni di classe ** n. – hvd

+0

@BryanChen È possibile limitare la modifica all'interno del modello di classe D principale, cioè senza l'aggiunta di specializzazione e duplicazione di codice come 'using T :: f;'? – alexolut

5

Una variante della risposta di Bryan Chen che sembra più brutta, ma rende più facile per estendere a più controlli, e non richiede duplicare il codice che è condivisa tra D<type-with-f> e D<type-without-f>, è quello di utilizzare una catena ereditaria, in cui ogni fase controlla un membro aggiuntivo. L'unica duplicazione richiesta è l'ereditarietà dei costruttori, se appropriato.

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 
} 

Nota: invece di controllare &T::f, si consiglia di fare qualcosa con std::declval<T>().f() invece. Il primo non può gestire funzioni sovraccariche.

+0

Hmm, potrei aver usato 'std :: integral_constant', ma molto bello. –

+0

@ T.C. Heh, questa è una delle cose che so essere lì, ma in qualche modo non finisco mai con l'uso, perché ogni volta che potrebbe essere utile, ho già trovato una soluzione nella mia mente che non ne ha bisogno. :) – hvd