2010-09-24 5 views
6

Je joue autour de type déduction/impression en utilisant des modèles avec le code de la forme:déduction de type de modèle de référence

#include <iostream> 
template <typename T> 
class printType {}; 

template <typename T> 
std::ostream& operator<<(std::ostream& os, const printType<T>&) 
{ 
    os << "SomeType"; return os; 
} 

template <typename T> 
std::ostream& operator<<(std::ostream& os, const printType<T*>&) 
{ 
    os << printType<T>() << "*"; return os; 
} 

template <typename T> 
std::ostream& operator<<(std::ostream& os, const printType<T&>&) 
{ 
    os << printType<T>() << "&"; return os; 
} 
// etc... and can call on a variable through 

template <typename T> 
printType<T> print(T) { return printType<T>(); } 

int main() 
{ 
    int a = 7; 
    int *p = &a; 
    int &r = a; 

    //OK: return SomeType* 
    std::cout << "type of p: " << print(p) << std::endl; 
    //Hmmmm: returns SomeType <- (no &: can I get around this?) 
    std::cout << "type of r: " << print(r) << std::endl; 
} 

Je me demande si obtenir la dernière ligne ou non je peux pour revenir int&, c'est-à-dire, soit:
(i) que le modèle de fonction print en déduit le type de son argument comme int& ou en quelque sorte fonctionne il devrait renvoyer un printType<T&> quand je le passe; ou
(ii) si cela est inévitable en raison de la façon dont la variable est transmise à la fonction. Y at-il des moyens de contourner cela en changeant la forme d'impression ou en utilisant une autre tricherie de gabarit? Si des solutions existent, je préférerais non-C++ 0x, mais il est toujours bon de voir quelles raccourcis seront, sinon déjà, disponibles dans le futur.

Répondre

9

Il n'y a aucun moyen de contourner ce problème. Une expression p, où p nomme une référence, a toujours le type auquel la référence fait référence. Aucune expression n'a jamais le type T&. Vous ne pouvez donc pas détecter si une expression provient d'une référence ou non.

Cela ne peut pas non plus être fait avec C++ 0x. C'est un principe profond de C++ qu'il n'y a pas d'expressions qui ont un type de référence. Vous pouvez écrire decltype(r) pour obtenir le type de ce que r noms au lieu de quel type l'expression r a. Mais vous ne serez pas en mesure d'écrire print(r), sauf si print est une macro bien sûr, mais je ne vois pas pourquoi vous iriez cette route horrible.

+0

Je suggère qu'il triche ... voir ci-dessous. –

1

Je prends ce que j'ai dit précédemment. Je pense que je pourrais avoir un moyen de faire ce travail en c/C++ pur, quoique d'une manière très en désordre. Vous besoin de passer un pointeur dans vos fonctions ...

-à-dire bool hello_world (std :: string & my_string, const std :: string * const my_string_ptr) {

bool hello_world (std :: chaîne my_string, const std :: string * const my_string_ptr) {

si vous maintenant testé

si (& my_string == my_string_ptr)

Il évaluerait vrai si le var était passé par référence, et faux si passé par valeur.

Bien sûr doubler vos variables dans toutes vos fonctions est sans doute pas la peine ...


Johannes est juste ... pas dans C++ pur. Mais vous pouvez le faire. L'astuce consiste à tricher. Utilisez un langage de script intégré comme perl pour rechercher votre source. Voici un module Perl intégré:

http://perldoc.perl.org/perlembed.html

Faites passer le nom de la fonction, nom de la variable, et l'emplacement de la source, puis utiliser une expression rationnelle pour trouver la variable et vérifier son type. Vraiment, cela pourrait être une meilleure solution pour votre code en général, en supposant que vous aurez toujours une source à portée de main.

Je vais poster une fonction pour cette approche de base dans un peu ... dois prendre soin de certains travaux du matin! :)

Même si vous ne voulez pas distribuer la source, vous pouvez créer une sorte de fichier de données fonction/var compressé que vous pourriez analyser dans @ runtime et obtenir un résultat équivalent.


Modifier 1

Par exemple ... en utilisant la fonction # I32 match(SV *string, char *pattern) dans le tutoriel Intégrer Perl, vous pouvez faire quelque chose comme:

bool is_reference(const char * source_loc, const char * function_name, 
        const char * variable_name) { 
    std::ifstream my_reader; 
    char my_string[256]; 
    SV * perl_line_contents; 
    bool ret_val = false; 
    char my_pattern [400]=strcat("m/.*",function_name); 
    my_pattern=strcat(my_pattern, ".*[,\s\t]*"); 
    my_pattern=strcat(my_pattern, variable_name); 
    my_pattern=strcat(my_pattern, "[\s\t]*[\(,].*$"); 

    my_reader.open(source_loc.c_str()); 
    while (!my_reader.eof()) { 
     my_reader.getline(my_string,256); 
     sv_setpv(perl_line_contents,my_string); 
     if(match(perl_line_contents,my_pattern)) { 
      ret_val= true; 
     } 
    } 

    return ret_val; 
} 

... il .. Deux façons de le faire (voir la mise à jour ci-dessus).

1

Vous pouvez utiliser SFINAE avec des types entiers ou tout ce que vous pouvez convertir « 0 » de cette façon:

template <typename T> 
class is_reference 
{ 
    struct yes { char a, b; }; 
    typedef char no; 

    template <typename U> static no test(T y) { } 
    template <typename U> static yes test(...) { } 

public: 
    static const bool value = sizeof(test<T>(0)) == sizeof(yes); 
}; 

#include <iostream> 

struct not_constructable_from_int {}; 
struct constructable_from_int { constructable_from_int(int x) { } }; 

int 
main() 
{ 
    std::cout << is_reference<int>::value << std::endl; 
    std::cout << is_reference<int&>::value << std::endl; 

    std::cout << is_reference<not_constructable_from_int>::value << std::endl; 
    std::cout << is_reference<not_constructable_from_int&>::value << std::endl; 
    std::cout << is_reference<constructable_from_int>::value << std::endl; 
    std::cout << is_reference<constructable_from_int&>::value << std::endl; 
} 

Notez que le test est essentiellement « pouvez-vous appeler test<T>(0) » que vous ne pouvez pas si T est une référence ou si T est n'importe quelle classe que vous ne pouvez pas convertir de 0. Malheureusement, vous pouvez toujours appeler test<T>(T()) ce qui m'a surpris (même si T est int).

Comme vous pouvez le voir si vous êtes prêt à faire vos types de int puis constructible le test fonctionne sur eux, ce qui me confond vraiment donné le résultat test<T>(T()) ...

Questions connexes