Question

Je suis en train d'apprendre les fonctionnalités actuellement acceptées 11 c ++ et je vais avoir des problèmes avec auto et decltype. Comme un exercice d'apprentissage Je l'extension de la liste des classes std avec quelques fonctions génériques.

template<class _Ty, class _Ax = allocator<_Ty>>
class FList : public std::list<_Ty, _Ax>
{
public:
    void iter(const function<void (_Ty)>& f)
    {
        for_each(begin(), end(), f);
    }

    auto map(const function<float (_Ty)>& f) -> FList<float>*
    {
        auto temp = new FList<float>();

        for (auto i = begin(); i != end(); i++)
            temp->push_back(f(*i));

        return temp;
    }
};

auto *ints = new FList<int>();
ints->push_back(2);
ints->iter([](int i) { cout << i; });

auto *floats = ints->map([](int i) { return (float)i; });
floats->iter([](float i) { cout << i; });

Pour la carte de membre Je veux que le type de retour d'être générique selon ce que la fonction retourne passé. Donc, pour le type de retour que je pouvais faire quelque chose comme ça.

auto map(const function<float (_Ty)>& f) -> FList<decltype(f(_Ty))>*

Il faudrait également supprimer le type de flotteur dans le modèle de fonction.

auto map(const function<auto (_Ty)>& f) -> FList<decltype(f(_Ty))>*

Je pourrais utiliser une classe de modèle, mais qui rend l'utilisation d'instances plus bavard que je dois spécifier le type de retour.

template<class T> FList<T>* map(const function<T (_Ty)>& f)

Pour résumer ma question que je vais essayer de comprendre comment définir la carte sans utiliser une classe de modèle et ont encore générique dans le type retourne.

Était-ce utile?

La solution

dérivant de std::list ou d'autres conteneurs de std:: est déconseillée.

Écrivez vos opérations fonctions libres afin qu'ils puissent travailler sur un conteneur standard via itérateurs.

Voulez-vous dire « définir la carte sans utiliser une fonction de modèle »?

Vous devriez être en mesure d'utiliser le type de membre result_type de std::function pour obtenir le type retourne.

Il est également pas nécessaire pour vous de spécifier que la fonction est passée en std::function. Vous pouvez la laisser ouverte comme tout type, et laisser le compilateur tout se rejoignent. Vous avez seulement besoin std::function pour le polymorphisme d'exécution.

Et en utilisant les nouvelles pour créer des objets premières tas d'allocation et de les renvoyer par le pointeur est soooo 1992! :)

Votre fonction iter est essentiellement la même chose que le Range- sur la base de la boucle .

Mais tout cela de côté ... voulez-vous dire quelque chose comme ça?

template <class TFunc>
auto map(const TFunc &f) -> FList<decltype(f(_Ty()))>*
{
    auto temp = new FList<decltype(f(_Ty()))>();

    for (auto i = begin(); i != end(); i++)
        temp->push_back(f(*i));

    return temp;
}

Cela correspond tout appelable, et déterminer le type de retour de la fonction en utilisant decltype.

Notez qu'il faut être _Ty défaut constructible. Vous pouvez contourner ce en fabriquant une instance:

template <class T>
T make_instance();

Pas de mise en œuvre est nécessaire, car aucun code est généré qui l'appelle, de sorte que l'éditeur de liens n'a rien à se plaindre (grâce à dribeas pour avoir signalé!)

Ainsi, le code devient:

FList<decltype(f(make_instance<_Ty>()))>*

Ou, littéralement, une liste de ce que le type serait vous obtiendrez d'appeler la fonction f avec une référence à une instance de _Ty.

Et comme un bonus gratuit pour accepter, rechercher des références rvalue - celles-ci signifie que vous pouvez écrire:

std::list<C> make_list_somehow()
{
    std::list<C> result;
    // blah...
    return result;
}

Et puis appelez comme ceci:

std::list<C> l(make_list_somehow());

Parce que std :: liste aura un « constructeur move » (comme un constructeur de copie, mais choisi lorsque l'argument est temporaire, comme ici), il peut voler le contenu de la valeur de retour, à savoir faire la même chose comme optimale swap. Donc, il n'y a pas de copie de la liste complète. (Ceci est la raison pour laquelle C ++ 0x fera exécuter le code existant écrit plus rapide naïvement - de nombreuses astuces de performance populaires, mais laid deviendra obsolète).

Et vous pouvez obtenir le même genre de chose gratuitement pour toute catégorie existante de votre propre, sans avoir à écrire un constructeur de mouvement correct, en utilisant unique_ptr.

std::unique_ptr<MyThing> myThing(make_my_thing_somehow());

Autres conseils

Vous ne pouvez pas utiliser automatiquement dans les arguments de la fonction où vous voulez que les types des arguments à déduire. Vous utilisez des modèles pour cela. Jettes un coup d'oeil à: http://thenewcpp.wordpress.com/2011/10/18/ le mot-clé auto / http://thenewcpp.wordpress.com/2011/10/25/ decltype-et declval / . Ils ont tous deux expliquent comment utiliser auto et decltype. Ils devraient vous donner assez d'informations sur la façon dont ils sont utilisés. En particulier, une autre réponse au sujet make_instance pourrait se faire mieux avec declval.

Je pense que le point que Jarrah a fait dans sa réponse est que ces points n'expliquent exactement comment utiliser ces choses. Je soulignerai les détails de toute façon:

Vous ne pouvez pas faire cela, il y a deux choses mal:

auto map(const function<auto (_Ty)>& f) -> FList<decltype(f(_Ty))>*

auto ne peut pas être utilisé pour les arguments de la fonction. Si vous voulez que le type de la fonction à déduire alors vous devriez utiliser des modèles. La seconde est que decltype prend une expression, tandis que _Ty est un type. Voici comment résoudre:

template <typename Ret>
auto
map(const function<Ret (_Ty)>& f)
  -> FList<decltype(f(declval<_Ty>()))>
{}

De cette façon, il n'y a pas de magie pour créer une instance d'un type.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top