web-dev-qa-db-fra.com

La déduction de modèle a échoué sur le tas mais fonctionne sur la pile

J'ai un problème avec une classe basée sur des modèles. Quand j'instancie la classe sur le pile, ça marche. Quand j'instancie la même classe sur le tas, ça échoue. (Déduction d'argument) Je ne comprends pas pourquoi ...

Infos: J'utilise gcc 7.2.0 avec c ++ 17.

voici un exemple:

#include <iostream>
#include <cstdlib>
#include <memory>

template <class ReturnType, class ClassName, class... Args>
class MethodPtr
{
public:
    typedef ReturnType (ClassName::*Method)(Args...);

    MethodPtr(ClassName* ptr, Method m) : _p(ptr), _m(m)
    {
        (ptr->*m)(4);
    }

    ClassName* _p;
    Method _m;
};

class Example
{
public:
    Example()
    {
        dotest(this, &Example::func);
    }

    template <class Ptr, class Func>
    void dotest(Ptr ptr, Func func)
    {
        // works
        MethodPtr(ptr, func);

        // don't works
        //std::make_unique<MethodPtr>(ptr, func);
        //new MethodPtr(ptr, func);

        //works
        std::make_unique<decltype(MethodPtr(ptr, func))>(ptr, func);
        new decltype(MethodPtr(ptr, func))(ptr, func);
    }

    void func(int i)
    {
        std::cout << i << std::endl;
    }
};

int main()
{
    Example example;
}

Avez-vous une solution pour éviter le decltype?

Merci,

25
user9098083

Le fait que new MethodPtr(ptr, func) échoue à la déduction est en effet un bogue du compilateur. Selon [dcl.type.class.deduct]/2 :

Un espace réservé pour un type de classe déduit peut également être utilisé dans le type-specifier-seq dans le nouveau type -id ou type-id d'une nouvelle-expression , ou comme spécificateur de type simple dans une conversion de type explicite (notation fonctionnelle) ([expr.type.conv] ). Un espace réservé pour un type de classe déduit ne doit apparaître dans aucun autre contexte.

Comme vous pouvez le voir, un oui explicite pour une nouvelle expression et une interdiction générale pour tout ce qui n'est pas explicitement autorisé. Donc make_unique ne peut pas recevoir un espace réservé.

Sauf si vous pouvez migrer vers une version GCC où cela a été corrigé (ou si vous avez juste besoin d'utiliser make_unique), vous ne pouvez pas éviter decltype. Essayez d'introduire un alias de type pour atténuer les inconvénients.

21
StoryTeller

Dans les derniers gcc et clang, cela fonctionne - cette new MethodPtr(ptr, func). Donc pour gcc7.2 - c'est un bug.

Pour unique_ptr(new MethodPtr(ptr, func)) - cela ne peut pas fonctionner - car en C++ - à ce niveau, avoir MethodPtr* - nous ne pouvons pas distinguer entre unique_ptr<MethodPtr[]> et unique_ptr<MethodPtr> - donc il ne peut pas être déduit.

6
PiotrNycz

Regardez ici . Passer simplement MethodPtr (modèle de classe) en tant que paramètre de type non modèle du modèle de fonction (std::make_unique) N'a jamais été autorisé, et la déduction d'argument du modèle de classe n'a pas changé cela.

new MethodPtr{ptr, func}; Fonctionne, en regardant la référence, mais je ne peux pas dire la raison pour laquelle cela devrait être différent de new MethodPtr(ptr, func);

3
LogicStuff