Question

Je voudrais pouvoir utiliser la déduction de modèle pour obtenir les résultats suivants:

GCPtr<A> ptr1 = GC::Allocate();
GCPtr<B> ptr2 = GC::Allocate();

au lieu de (ce que j'ai actuellement):

GCPtr<A> ptr1 = GC::Allocate<A>();
GCPtr<B> ptr2 = GC::Allocate<B>();

Mon actuelle fonction Allouer ressemble à ceci:

class GC
{
public:
    template <typename T>
    static GCPtr<T> Allocate();
};

Serait-ce possible de faire tomber le <A> supplémentaire et <B>?

Était-ce utile?

La solution

Cela ne peut être fait. Le type de retour ne prend pas part à la déduction de type, il est plutôt le résultat d'avoir déjà assorti la signature de modèle approprié. Vous pouvez néanmoins cacher la plupart des utilisations comme:

// helper
template <typename T>
void Allocate( GCPtr<T>& p ) {
   p = GC::Allocate<T>();
}

int main()
{
   GCPtr<A> p = 0;
   Allocate(p);
}

Si cette syntaxe est en fait mieux ou pire que la GCPtr<A> p = GC::Allocate<A>() initiale est une autre question.

P.S. c ++ 11 vous permettra de sauter l'une des déclarations de type:

auto p = GC::Allocate<A>();   // p is of type GCPtr<A>

Autres conseils

La seule chose que je peux penser à: faire Allouer un non-modèle qui renvoie un objet proxy non-modèle qui a un opérateur de conversion basé sur un modèle qui fait le vrai travail:

template <class T>
struct GCPtr
{

};

class Allocator
{
public:
    template <class T>
    operator GCPtr<T>() { return GCPtr<T>(); }
};

class GC
{
public:
    static Allocator Allocate() { return Allocator(); }//could give a call-back pointer?
};

int main()
{
    GCPtr<int> p = GC::Allocate();
}

Vous pouvez aller le chemin inverse.

Si vous utilisez un compilateur jusqu'à date (MSVC 2010 qui devrait être dans quelques jours, ou la version actuelle de GCC) et ne me dérange pas compter sur C ++ 0x caractéristiques:

auto ptr1 = GC::Allocate<A>();
auto ptr2 = GC::Allocate<B>();

vous sauverait la <A> supplémentaire et <B>, mais pas sur le côté droit. :)

(Cette réponse est la même que celle @UncleBens, mais un peu plus général comme parfait avancer des arguments.)

Ceci est très utile dans des langues comme haskell où, par exemple, read aura une chaîne de caractères en tant qu'entrée et l'analyser en fonction du type de retour souhaitée.

(Voici le code exemple ideone.)

Tout d'abord, commencez par la fonction foo dont le type de retour, nous voulons déduire:

template<typename Ret>
Ret foo(const char *,int);
template<>
std::string foo<std::string>(const char *s,int) { return s; }
template<>
int         foo<int        >(const char *,int i) { return i; }

Lorsqu'on lui a demandé une chaîne, il retournera la chaîne qui est dans son premier argument. Lorsqu'on lui a demandé un entier, il retournera le second argument.

Nous pouvons définir une auto_foo fonction qui peut être utilisé comme suit:

int main() {
        std::string s = auto_foo("hi",5); std::cout << s << std::endl;
        int         i = auto_foo("hi",5); std::cout << i << std::endl;
}

Pour faire ce travail, nous avons besoin d'un objet qui permet de stocker temporairement les arguments de la fonction, et appelle aussi la fonction quand il est invité à convertir le type de retour souhaitée:

#include<tuple>

template<size_t num_args, typename ...T>
class Foo;
template<typename ...T>
class Foo<2,T...> : public std::tuple<T&&...>
{
public: 
        Foo(T&&... args) :
                std::tuple<T&&...>(std::forward<T>(args)...)
        {}
        template< typename Return >
        operator Return() { return foo<Return>(std::get<0>(*this), std::get<1>(*this)); }
};
template<typename ...T>
class Foo<3,T...> : std::tuple<T&&...>
{
public: 
        Foo(T&&... args) :
                std::tuple<T&&...>(std::forward<T>(args)...)
        {}
        template< typename Return >
        operator Return() { return foo<Return>(std::get<0>(*this), std::get<1>(*this), std::get<2>(*this)); }
};

template<typename ...T>
auto
auto_foo(T&&... args)
        // -> Foo<T&&...> // old, incorrect, code
        -> Foo< sizeof...(T), T&&...> // to count the arguments
{
        return              {std::forward<T>(args)...};
}

En outre, les travaux ci-dessus pour deux arg ou trois fonctions arg, il est difficile de ne pas voir comment étendre cela.

Ceci est beaucoup de code à écrire! Pour chaque fonction sera d'appliquer cela, vous pouvez écrire une macro qui fait cela pour vous. Quelque chose comme cela en haut de votre fichier:

REGISTER_FUNCTION_FOR_DEDUCED_RETURN_TYPE(foo); // declares
                        // necessary structure and auto_???

et vous pouvez ensuite utiliser auto_foo dans votre programme.

De la même façon, vous ne pouvez pas surcharger les fonctions du type de retour, vous ne pouvez pas déduire de modèle sur elle. Et pour la même raison - si f () est un modèle / surcharge qui retourne quelque chose, quel type à utiliser ici:

f();

Vous pouvez essayer d'utiliser une macro pour elle. A part cela, je ne vois pas comment cela est censé fonctionner avec une seule déclaration.

#define ALLOC(ptrname,type) GCPtr<type> ptrname = GC::Allocate<type>()

ALLOC(ptr1,A);

points de Johannes sont valides. Le >> problème est facilement corrigé. Mais je pense avoir des virgules dans le cadre du type nécessite le préprocesseur C99 varargs extension:

#define ALLOC(ptrname,...) GCPtr< __VA_ARGS__ > ptrname = GC::Allocate< __VA_ARGS__ >()

ALLOC(ptr1,SomeTemplate<int,short>);
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top