2012-03-10 7 views
6

W moim projekcie chcę zaimplementować szablonową klasę proxy niektórych istniejących większych klas. Istniejące klasy są klasami bibliotek, więc nie można ich modyfikować. W większości przypadków klienci nie wiedzą, że obiekty są instancjami klasy proxy lub większej klasy. W niektórych przypadkach klienci MUSZĄ jednak znać szczegółowe informacje o klasie. Ponieważ klasa proxy sama jest klasą szablonów, nie sądzę, aby proste przeciążanie funkcji przez nazwę klasy mogło rozwiązać ten problem. Możliwe rozwiązanie, które moim zdaniem polega na dodaniu wewnętrznej klasy zagnieżdżonej lub typedef wewnątrz klasy proxy, a klient sprawdza, czy ta klasa/typedef istnieje, aby uzyskać informacje o klasie. Moje pytanie brzmi: jak sprawdzić, czy klasa określiła zagnieżdżoną definicję klasy lub typedef w C++ 11?Jak sprawdzić, czy klasa określiła zagnieżdżoną definicję klasy lub typedef w C++ 11?

poniższych kodów pokazują przykład:

#include <iostream> 
#include <functional> 
#include <string> 
#include <vector> 
#include <type_traits> 

typedef std::string CBig1; // use string for demonstration 
typedef std::string CBig2; // use string for demonstration 

//class CBig1; // the bigger class 1, codes of which can not be changed 
//class CBig2; // the bigger class 2, codes of which can not be changed 

template <typename _Big, typename _Other> 
class CProxy 
{ 
public: 
    struct proxy_tag { }; 
}; 

// how to implement this ? 
// the proxy traits class, if defined _T::proxy_tag, the ``type'' will be std::true_type, otherwise the ``type'' will be std::false_type 
template <typename _T> 
struct is_proxy 
{ 
    //typedef std::true_type type; 
    //typedef std::false_type type; 
}; 

template <typename _T> 
void ClientHelp(const _T& t, std::false_type) 
{ 
    // process real class 
    std::cerr << "real class" << std::endl; 
} 

template <typename _T> 
void ClientHelp(const _T& t, std::true_type) 
{ 
    // process proxy class 
    std::cerr << "proxy class" << std::endl; 
} 

template <typename _T> 
void Client(const _T& t) 
{ 
    ClientHelp(t, typename is_proxy<_T>::type()); 
} 

int main(int argc, char* argv[]) 
{ 
    CBig1 b; 
    CProxy<CBig1, int> p; 
    Client(b); 
    Client(p); 
    return 0; 
} 

jak zaimplementować klasę cech is_proxy?

+0

Użyj [SFINAE] (http://en.wikipedia.org/wiki/SFINAE). – iammilind

+3

Nie używaj nazw takich jak '_To', są one zarezerwowane dla implementacji. – Xeo

Odpowiedz

10

Jako uzupełnienie do wersji C++ 03, w C++ 11 można dostać decltype:

template <typename T> 
auto is_proxy(T const&) -> decltype(T::proxy_tag{}, std::true_type{}) { 
    return std::true_type{}; 
} 

std::false_type is_proxy(...) { return std::false_type{}; } 

A twój realizacja Client staje:

template <typename T> 
void Client(T const& t) { 
    ClientHelp(t, is_proxy(t)); 
} 

słodki, nie jest to ?

+0

To nie kompiluje się dla mnie (dość niedawny głos ++ z SVN). Problem wydaje się być ** dwoma ** argumentami dla 'decltype()'. Jak to ma działać? –

+3

@MichaelWild: używa operatora sekwencji ','. Zwykle znajduje się on w warunkach pętli, takich jak 'for (;; +, i, ++ e)', operator sekwencji ocenia oba operandy od lewej do prawej i zwraca wynik prawego argumentu. Można go również powiązać: '++ a, ++ b, ++ c' zwróci wynik' ++ c', a ty masz gwarancję, że kolejność oceny to '++ a' * then *' + + b' * then * '++ c'. W tym przypadku jedynym zastosowaniem jest posiadanie po lewej stronie sprawdzenia SFINAE, a po prawej wyrażenie wyrażające pożądany typ zwrotu. Możesz spróbować owijać całość w kolejną warstwę nawiasów. –

+0

Podejrzewałem tak samo (nie byłem pewien, jak kompilator wykonywał analizę). Być może muszę dodać dodatkowe nawiasy? Ale potem AFAIK, "decltype" jest dość osobliwe, jeśli chodzi o nawias ... –

16

Można użyć lekki typu kategoryzacji idiom

template<class T, class R = void> 
struct enable_if_type { typedef R type; }; 

template<class T, class Enable = void> 
struct test : std::false_type {}; 

template<class T> 
struct test<T, typename enable_if_type<typename T::is_proxy_tag>::type> : std::true_type 
{}; 

template <typename _Big, typename _Other> 
class CProxy 
{ 
    public: 
    typedef void is_proxy_tag; 
}; 

Więc dokonać klasę pełnomocnika, po prostu dodaj ten

typedef void is_proxy_tag; 

i SFINAE w enable_if_type wybierze właściwą true_type/false_type specjalizacji

Należy pamiętać, że używanie boost::mpl::true_ zamiast std::true_type itd. Sprawia, że ​​to rozwiązanie działa dla C++ 03.

Powiązane problemy