2016-09-05 2 views
0

Ma situation:Comment ajouter utiliser un pointeur à la valeur de membre en tant que paramètre de modèle par type (pas de valeur)

je dois souvent d'avoir un vecteur de structures où un champ peut être considéré comme une clé ou carte d'identité , et plutôt que de le stocker de manière coûteuse dans une carte (l'utilisation de la mémoire est très importante dans cette application) Je veux le stocker dans un vecteur plat, mais présenter une interface de type carte pour trouver des éléments par clé.

Ma première solution à ce problème:

template <class T, class Key, class KeyFn> 
class TKeyedVector : public std::vector<T> 
{ 
public: 

    const_iterator  find(const Key& key) const {return std::find_if(begin(), end(), [&](const T& entry) {return keyFn(entry)==key; }); } 

    KeyFn keyFn; 
}; 

struct KeyedDataEntry 
{ 
    std::string  key; 
    int    value; 

    struct KeyExtractor { 
    const std::string& operator()(const KeyedDataEntry& e) const {return e.key; }; 
    }; 
}; 

using KeyedDataArray = TKeyedVector<KeyedDataEntry, std::string, KeyedDataEntry::KeyExtractor>; 

Maintenant tout cela fonctionne, mais je voudrais être en mesure d'éliminer la nécessité pour le type KeyExtractor en utilisant le pointeur vers la variable élément noyé dans le type :

template <class T, class Key, Key T::* keyFn> 
class TKeyedVector : public std::vector<T> 
{ 
public: 
     const_iterator  find(const Key& key) const {return std::find_if(begin(), end(), [&](const T& entry) {return keyFn(entry)==key; }); } 
}; 

using KeyedDataArray = TKeyedVector<KeyedDataEntry, std::string, &KeyedDataEntry::key>; 

Cependant, je n'arrive pas à faire fonctionner cela. J'ai regardé la mise en œuvre de std::mem_fn pour des indices, mais je ne peux pas savoir comment le faire. L'erreur que je reçois est quelque chose comme:

warning C4353: nonstandard extension used: constant 0 as function expression. Use '__noop' function intrinsic instead 

Des indices?

EDIT: Version échantillon à http://ideone.com/Qu6TEy

Répondre

1

Voici le début d'une solution de travail. Vous n'avez pas besoin d'un objet extracteur spécial.

Notez que j'ai encapsulé le vecteur. Avec le temps, vous regretterez de ne pas le faire.

#include <vector> 
#include <string> 

template <class T, class Key, const Key& (T::*Extractor)() const> 
class TKeyedVector 
{ 
    using storage = std::vector<T>; 
    using const_iterator = typename storage::const_iterator; 
public: 

    decltype(auto) begin() const 
    { 
     return storage_.begin(); 
    } 

    decltype(auto) end() const 
    { 
     return storage_.end(); 
    } 

    const_iterator find(const Key& key) const 
    { 
     return std::find_if(begin(), 
          end(), 
          [&](const T& entry) 
     { 
      return entry.*Extractor() == key; 
     }); 
    } 

    storage storage_; 
}; 

struct KeyedDataEntry 
{ 
    std::string  key; 
    int    value; 

    const std::string& get_key() const { return key; } 

}; 

int main() 
{ 
    TKeyedVector<KeyedDataEntry, std::string, &KeyedDataEntry::get_key> mymap; 

} 

Mais il y a un problème avec votre idée.

Pour que cette structure soit une carte, les clés doivent être immuables. Ceci plaide pour seulement renvoyer des objets immuables. Cela plaide alors immédiatement pour simplement utiliser un unordered_set ou set.

Si vous souhaitez renvoyer des références à des objets mutables dans le vecteur sous-jacent, vous pouvez tout simplement utiliser std::find_if avec un prédicat pour les trouver.

+0

Merci - cela m'a amené à la solution dont j'avais besoin. La clé était la syntaxe pour la définition de l'extracteur dans le modèle et ensuite comment l'extracteur est déréférencé dans la fonction 'find()'. Dans le cas présent, l'immuabilité des clés n'est pas un problème car elle ne doit pas fonctionner totalement comme une carte - je veux juste pouvoir trouver des objets par un champ de type ID. –

1

Un pointeur vers un membre nécessite le pointeur sur la syntaxe d'appel de membre. C12 17 viendra avec une fonction standard std::invoke pour rendre l'écriture de tels modèles un peu moins fastidieuse (cela fonctionnera pour tous les objets appelables). Mais en attendant, voici comment vous devez faire cela.

+0

J'ai essayé de m'adapter à cette syntaxe, mais pas de chance (voir le lien d'ideone ci-dessus). L'erreur est: 'prog.cpp: 15: 29: erreur: aucune correspondance pour l'appel à '(const string {alias const std :: basic_string })()' return (entrée. * KeyFn)() == clé ; ' –

+0

@the_mandrill vous définissez le type de retour du membre comme' std :: string' mais passez un membre qui retourne 'const std :: string &'. Ce n'est pas lié à la question en question. Vous devez définir une classe de traits pour ajuster le type de retour. – StoryTeller