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