I'm implementing a lazy constructor in C++. The goal is that for a type T
, lazy<T>(args...)
returns a callable object which, when called, returns T(args...)
.
This is what I have done so far:
#include <functional>
#include <utility>
/**
* Returns an object of type `T` constructed from `args`.
*/
template<typename T, typename... Args>
T make(Args&&... args)
{
return T(std::forward<Args>(args)...);
}
/**
* Wrap a value of type `T` and perfect-forward it when accessed.
*/
template<typename T>
class Forward {
public:
Forward(T &&value) noexcept : value_(std::forward<T>(value))
{}
operator T()
{
return std::forward<T>(value_);
}
private:
T value_;
};
/**
* Returns a callable object which, when called, returns
* an object of type `T` constructed from `args`.
*/
template<typename T, typename... Args>
auto lazy(Args&&... args) -> decltype(
std::bind(
make<T, Args...>,
Forward<Args>(std::forward<Args>(args))...))
{
return std::bind(
make<T, Args...>,
Forward<Args>(std::forward<Args>(args))...);
}
(Contrived) Examples:
// rvalue arguments
auto makePtr = lazy<std::unique_ptr<int>>(new int(123));
auto ptr = makePtr();
// lvalue arguments
int *rawPtr = new int(456);
auto makePtr2 = lazy<std::unique_ptr<int>>(rawPtr);
auto ptr2 = makePtr2();
Can this code be improved (particularly in terms of efficiency)?
UPDATE: The Forward
class is used only as arguments to std::bind
. Rationale:
Suppose we have a type Foo
with constructor Foo(const std::unique_ptr<int> &)
. We can construct it by calling make<Foo, const std::unique_ptr<int> &>(someUniquePtr)
, for example.
However, std::bind(make<Foo, const std::unique_ptr<int> &>, someUniquePtr)()
won't work (I think), because the reference-ness of the second argument to std::bind
will be dropped. Wrapping it inside a std::cref
works, but for packed arguments it seems the Forward
class is the only solution I can come up with that works for all types.
It's possible that I misinterpreted the cause of this but here is an example of what I mean: https://godbolt.org/g/8w2Msa.
value_
would get moved out and no longer be accessible via further calls tooperator T()
", but that's actually the point. Please see my update. \$\endgroup\$ – Zizheng Tai May 3 '16 at 6:36