Suppose we have a class Box as follows:
struct Base {}
template<typename T>
struct Box : Base
{
template<typename... Args>
Box(Args&&... args)
: t(forward<Args>(args)...)
{}
T t;
}
And then we have a function MakeBox:
template<typename X>
Base* MakeBox(X&& x)
{
return new Box<???>(forward<X>(x));
}
The type X
is deduced from the parameter used in the call to MakeBox.
We then need to calculate somehow from X the appropriate "storage type" parameter T.
I think if we just naively use:
return new Box<X>(forward<X>(x));
then this will cause problems.
Clearly std::bind
and std::function
need to deal with these problems, how do they do it?
Is std::decay
helpful in anyway here?
If I understand correctly what you want to achieve, then you need to use std::decay
. Supposing you are supplying an object of type S
to MakeBox()
, the universal reference X&&
will be resolved in such a way to make the function argument either of type S&
or S&&
depending on whether your argument is (respectively) an lvalue or an rvalue.
To achieve this and due to C++11 rules for universal references, in the first case the template argument will be deduced as X=S&
(here X
would not be OK as an argument to Box<>
, because your member variable has to be an object and not an object reference), while in the second case it will be deduced as X=S
(here X
would be fine as an argument to Box<>
). By applying std::decay
you will also implicitly apply std::remove_reference
to the deduced type X
before supplying it as a template argument to Box<>
, you are going to make sure that X
will always be equal S
and never S&
(please keep in mind, that X
is never going to be deduced as S&&
here, it will be either S
or 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;
}
If you are interested, here is a very good lesson by Scott Meyers where he explains how universal references behave:
Scott Meyers on universal references
P.S.: This answer has been edited: my original answer suggested to use std::remove_reference<>
, but std::decay
turned out to be a better choice. Credits to the question poster @Andrew Tomazos FathomlingCorps, who pointed that out, and to @Mankarse, who first proposed it in a comment to the original question.
For the provided example (1)
does what you want and stores the value. For other cases (such as when x
is an array) you can use std::decay
to decay it to a pointer and store that.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With