There are similar questions, but I did not find an answer that works for my problem.
Consider the following code:
#include <cassert>
#include <functional>
#include <iostream>
#include <memory>
#include <utility>
class TestClass
{
public:
TestClass( int value): mValue( value) { }
private:
int mValue;
};
template< typename T> class DeferredCreator
{
public:
template< class... Args> DeferredCreator( Args&&... args):
mpCreator( [=]() -> T*
{ return new T( std::forward< Args>( args)...); }
),
mpObject()
{ }
T* get() {
if (mpObject == nullptr)
mpObject.reset( mpCreator());
return mpObject.get();
}
private:
std::function< T*( void)> mpCreator;
std::unique_ptr< T> mpObject;
};
int main() {
DeferredCreator< int> dcInt( 42);
assert( dcInt.get() != nullptr);
return 0;
}
The idea is that the class DeferredCreator creates an object only when it is really needed. I got this work e.g. for strings, but I can't figure out how to pass a simple integer into my lambda.
The error message I get is:
prog.cpp:19:26: error: no matching function for call to 'forward'
{ return new T( std::forward< Args>( args)...); }
^~~~~~~~~~~~~~~~~~~
prog.cpp:36:27: note: in instantiation of function template specialization 'DeferredCreator<int>::DeferredCreator<int>' requested here
DeferredCreator< int> dcInt( 42);
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/6.3.0/../../../../include/c++/6.3.0/bits/move.h:76:5: note: candidate function not viable: 1st argument ('const int') would lose const qualifier
forward(typename std::remove_reference<_Tp>::type& __t) noexcept
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/6.3.0/../../../../include/c++/6.3.0/bits/move.h:87:5: note: candidate function not viable: 1st argument ('const int') would lose const qualifier
forward(typename std::remove_reference<_Tp>::type&& __t) noexcept
^
2 errors generated.
I already tried to use decltype( args)
as template argument for std::forward<>
, but that did not help.
The code is also available here: https://ideone.com/MIhMkt
args...
is constant because a lambda's call operator is implicitly const
. So, if you make your lambda mutable, then it works:
[=]() mutable -> T*
{ return new T( std::forward< Args>( args)...); }
The reason it didn't work with decltype(args)
is that the types themselves are not const
, just the call operator.
The operator()
of the closure type generated by your lambda expression is const
-qualified. std::forward
can attempt to move args...
, which are data members of the closure. const
objects cannot be moved.
You can mark your lambda as mutable
:
mpCreator( [=]() mutable -> T*
{ return new T( std::forward< Args>( args)...); }
),
This removes the implicit const
qualfiier from the closure type's generated operator()
.
live example on wandbox.org
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