5 votes

Obtenir le type de valeur de stockage approprié ?

Supposons que nous ayons une classe Box comme suit :

struct Base {}

template<typename T>
struct Box : Base
{
    template<typename... Args>
    Box(Args&&... args)
        : t(forward<Args>(args)...)
    {}

    T t;
}

Nous avons ensuite une fonction MakeBox :

template<typename X>
Base* MakeBox(X&& x)
{
    return new Box<???>(forward<X>(x));
}

Le type X est déduit du paramètre utilisé dans l'appel à MakeBox.

Nous devons ensuite calculer d'une manière ou d'une autre, à partir de X, le paramètre "type de stockage" approprié T.

Je pense que si nous utilisons naïvement :

    return new Box<X>(forward<X>(x));

cela posera des problèmes.

En clair, il s'agit de std::bind y std::function doivent faire face à ces problèmes, comment s'y prennent-ils ?

Est std::decay utile en quoi que ce soit ?

3voto

Andy Prowl Points 62121

Si je comprends bien ce que vous voulez faire, vous devez utiliser std::decay . Supposons que vous fournissiez un objet de type S a MakeBox() la référence universelle X&& sera résolu de manière à ce que l'argument de la fonction soit du type S& o S&& selon que votre argument est (respectivement) une valeur l ou une valeur r.

Pour ce faire, et en raison des règles du C++11 concernant les références universelles, dans le premier cas, l'argument du modèle sera déduit comme suit X=S& (ici X ne serait pas acceptable en tant qu'argument de Box<> car votre variable membre doit être un objet et non une référence d'objet), tandis que dans le second cas, elle sera déduite comme suit X=S (ici X serait parfait en tant qu'argument de Box<> ). En appliquant std::decay vous appliquerez aussi implicitement std::remove_reference au type déduit X avant de le fournir comme argument de modèle à Box<> vous allez vous assurer que X sera toujours égale S et jamais S& (il convient de garder à l'esprit que X est jamais va être déduite comme suit S&& ici, il s'agira soit S o S& ).

#include <utility>
#include <type_traits>
#include <iostream>

using namespace std;

struct Base {};

template<typename T>
struct Box : Base
{
    template<typename... Args>
    Box(Args&&... args)
        : t(forward<Args>(args)...)
    {
    }

    T t;
};

template<typename X>
Base* MakeBox(X&& x)
{
    return new Box<typename decay<X>::type>(forward<X>(x));
}

struct S
{
    S() { cout << "Default constructor" << endl; }
    S(S const& s) { cout << "Copy constructor" << endl; }
    S(S&& s) { cout << "Move constructor" << endl; }
    ~S() { cout << "Destructor" << endl; }
};

S foo()
{
    S s;
    return s;
}

int main()
{
    S s;

    // Invoking with lvalue, will deduce X=S&, argument will be of type S&
    MakeBox(s);

    // Invoking with rvalue, will deduce X=S, argument will be of type S&&
    MakeBox(foo());

    return 0;
}

Si vous êtes intéressé, voici une très bonne leçon de Scott Meyers qui explique comment les références universelles se comportent :

Scott Meyers sur les références universelles

P.S. : Cette réponse a été éditée : ma réponse initiale suggérait d'utiliser std::remove_reference<> mais std::decay s'est avéré être un meilleur choix. Merci à @Andrew Tomazos FathomlingCorps, qui l'a signalé, et à @Mankarse, qui l'a proposé pour la première fois dans un commentaire à la question originale.

1voto

yuri kilochek Points 3643

Pour l'exemple fourni (1) fait ce que vous voulez et stocke la valeur. Dans d'autres cas (par exemple lorsque x est un tableau), vous pouvez utiliser std::decay pour le décomposer en un pointeur et le stocker.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X