web-dev-qa-db-fra.com

Supprimer la référence dans decltype (retourner T au lieu de T & où T & est le decltype)

(Si vous êtes un pro C++ 11, passez au paragraphe en gras.)

Disons que je veux écrire une méthode de modèle qui appelle et renvoie le résultat d'un objet passé dont le type est le paramètre de modèle:

template<ReturnType, T>
ReturnType doSomething(const T & foo) {
    return foo.bar(); // EDIT: Might also be an expression introducing a temp val
}

Donc T doit avoir une méthode ReturnType T::bar() const pour être utilisée dans un appel comme celui-ci:

struct MyClass {
    ...
    int bar() const;
    ...
};
...
MyClass object;
int x = doSomething<int, MyClass>(object);

Nous n'avons pas besoin d'écrire MyClass grâce à la déduction de type et l'appel devient:

int x = doSomething<int>(object);

Mais omettre <int> Entraîne également une erreur de compilation car la méthode n'a pas besoin de retourner int pour être affectée à x par la suite (elle pourrait retourner char par exemple) .

En C++ 0x/11, nous avons les auto et decltype avec lesquels nous pouvons utiliser pour déduire le type de retour d'une méthode de modèle:

template<T>
auto doSomething(const T & foo) -> decltype(foo.bar()) {
    return foo.bar(); // EDIT: Might also be an expression introducing a temp val
}

Le compilateur va maintenant découvrir quel est le type de foo.bar() et l'utilise simplement comme type de retour. Avec notre classe concrète MyClass ce sera un int et ce qui suit suffirait:

int x = doSomething(object);

Maintenant à ma question:

Si MyClass définit bar() comme renvoyant un int&, Le type de retour de doSomething(object) sera également un int& = decltype(foo.bar()). C'est un problème, car G ++ se conforme maintenant que je renvoie une référence à temporaire .

Comment puis-je réparer cela? Y a-t-il quelque chose comme remove_reference Qui peut être utilisé comme remove_reference(decltype(foo.bar()))?

J'ai pensé à déclarer une méthode d'assistance qui prend un T& Et retourne un T puis définit le type de retour de doSomething à être decltype(helper(foo.bar())). Mais il doit y avoir un meilleur moyen, je le sens.

52
leemes

Pour supprimer une référence:

#include <type_traits>

static_assert(std::is_same<int, std::remove_reference<int&>::type>::value, "wat");

Dans ton cas:

template <typename T>
auto doSomething(const T& foo)
    -> typename std::remove_reference<decltype(foo.bar())>::type
{
    return foo.bar();
}

Juste pour être clair, notez que le retour écrit d'une référence est très bien:

#include <type_traits>

struct f
{
    int& bar() const
    {
        static int i = 0;
        return i;
    } 
};

template <typename T>
auto doSomething(const T& foo)
    -> decltype(foo.bar())
{ 
    return foo.bar();
}

int main()
{
    f x;
    return doSomething(x);
}

La référence retournée peut simplement être transmise sans erreur. Votre exemple dans le commentaire est où il devient important et utile:

template <typename T>
auto doSomething(const T& foo)
    -> decltype(foo.bar())
{ 
    return foo.bar() + 1; // oops
}
52
GManNickG