2017-03-16 1 views
3

Je suis en train de résoudre un problème classique: vérifier s'il existe une fonction libre dans un espace de noms. Il est discuté, par exemple, here.Méta-programmation de modèle: vérification de l'existence d'une fonction définie plus tard

Cependant, il y a une légère torsion: la définition de la fonction peut apparaître plus tard que la classe de vérificateur. Voici un exemple.

struct Yes {}; 
struct No {}; 
struct YesButLater {}; 

void f(Yes) {} 

template<typename T, typename Enable = void> 
struct HasF : public std::false_type {}; 

template<typename T> 
struct HasF<T, decltype(void(::f(T())))> : public std::true_type {}; 

void f(YesButLater) {} 

int main() { 
    cout << HasF<Yes>::value << endl; // 1 
    cout << HasF<No>::value << endl; // 0 
    cout << HasF<YesButLater>::value << endl; // 0, expected 1 
} 

f(YesButLater) est déclaré plus tard que HasF classe d'aide, et, bien que j'instancier le modèle après f(YesButLater) a été défini, l'aide ne remarque pas. Donc, voici la question 1: comment puis-je m'en occuper?

Maintenant, un exemple de plus, plus curieux.

template<typename T> 
struct HasF<T, decltype(void(f(T())))> : public std::true_type {}; 

void f(YesButLater) {} 
void f(std::string) {} 

int main() { 
    cout << HasF<YesButLater>::value << endl; // 1 (but what's the difference?) 
    cout << HasF<std::string>::value << endl; // 0, expected 1 
} 

Notez que j'ai enlevé :: de l'expression decltype(...). Maintenant, pour une raison quelconque f(YesButLater)est noté par HasF, mais f(std::string) reste encore obscur.

Question 2: Pourquoi observons-nous un comportement différent pour ::f(T()) et f(T()) dans cet exemple? De plus, quelle est la différence entre YesButLater et std::string?

Je pense qu'il y a un truc avec la recherche d'espace de noms mais je ne peux pas obtenir la chose.

+0

je pense - elle est due à [adl] règles (...) http://en.cppreference.com/w/cpp/language/adl –

+0

@ W.F. Oui, sans doute, mais je ne comprends toujours pas pourquoi :: les questions que je toujours utiliser espace de noms global dans l'exemple. –

Répondre

3

Il semble que j'ai compris ce qui se passe.

Quand j'écris ::f(...), le nom f est recherché avec un nom qualifié recherche. Ce type de recherche ne rencontre que des noms dont les déclarations étaient disponibles jusqu'au point d'appel. Maintenant, il est clair pourquoi la première version n'a pas pu trouver f(YesButLater): sa déclaration se produit plus tard.

Lorsque j'écris f(...), recherche de nom non qualifié se produit. Encore une fois, il ne trouve aucun nom qui a été déclaré avant l'appel du point. Ici argument dépendant recherche lève. Il recherche f(T) dans l'espace entier auquel appartient T. Dans le cas de f(YesButLater) cet espace de noms est global, d'où la fonction est trouvée. Dans le cas de f(std::string) ADL essaie de rechercher std::f(std::string) et, bien sûr, échoue.

Voici deux exemples pour illustrer le cas.

namespace foo { 
    class C {}; 
} 

template<typename T> 
void call() { 
    f(T()); 
} 

namespace foo { 
    void f(C) {} 
} 

int main() { 
    call<foo::C>(); 
} 

Ici f(T()) est recherché avec ADL et se trouve, même si sa déclaration est après call(). Et si nous modifions la fonction call() ...

template<typename T> 
void call() { 
    foo::f(T()); 
} 

Il en résulte une erreur de compilation parce que foo::f(T) effectue recherche qualifié et ne peut pas trouver la fonction nécessaire car aucune déclaration est disponible pour le moment.