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.