あなたが達成したいことを正しく理解していれば、 を使用する必要がありますstd::decay
。型のオブジェクトを に提供すると仮定するとS
、MakeBox()
ユニバーサル参照X&&
は、関数の引数を型にするか、引数が (それぞれ) 左辺値か右辺値かによってS&
解決されます。S&&
これを実現するため、およびユニバーサル参照の C++11 規則により、最初のケースでは、テンプレート引数は次のように推定されます (メンバー変数はオブジェクトではなくオブジェクトである必要があるため、X=S&
ここでX
は への引数としては OK ではありません)。Box<>
参照)、2 番目のケースではX=S
(ここでX
は への引数としては問題ありませんBox<>
) と推定されます。適用することにより、へのテンプレート引数として提供する前に推定された型にstd::decay
も暗黙的に適用されます。または_std::remove_reference
X
Box<>
X
S
S&
X
S&&
S
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;
}
興味がある場合は、Scott Meyers による非常に優れたレッスンがあり、ユニバーサル参照がどのように動作するかを説明しています。
普遍的な参照に関するスコット・マイヤーズ
PS:この回答は編集されました:私の最初の回答は を使用することを提案しましたが、より良い選択であることstd::remove_reference<>
がstd::decay
判明しました。それを指摘した質問投稿者@Andrew Tomazos FathomlingCorpsと、元の質問へのコメントで最初に提案した@Mankarseの功績。