I have two classes: Lattice and ModelGUI. I want to pass function from Lattice to GUI as a callback. I implemented Lattice as unique_ptr. Some code:
ModelGUI.h:
using CheckTypeClbk = std::function<Enums::AgentType(int)>;
ModelGUI(const Matrix* matrix_, CheckTypeClbk callback, float windowHeight_, float windowWidth_, float latticeWidth_);
main.cpp:
std::unique_ptr<ILattice> lattice(new Lattice(5, qMap));
ModelGUI gui(lattice->getLattice(), std::bind(&ILattice::checkAgentType, lattice, std::placeholders::_1),
800, 1200, 800);
With this implementation I got strange compilation errors about templates:
1>main.cpp
1>d:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.14.26428\include\xutility(390): error C2664: 'std::tuple<std::unique_ptr<ILattice,std::default_delete<_Ty>>,std::_Ph<1>>::tuple(std::tuple<std::unique_ptr<_Ty,std::default_delete<_Ty>>,std::_Ph<1>> &&)': cannot convert argument 1 from 'std::unique_ptr<ILattice,std::default_delete<_Ty>>' to 'std::allocator_arg_t'
1> with
1> [
1> _Ty=ILattice
1> ]
1>d:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.14.26428\include\xutility(389): note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
1>d:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.14.26428\include\functional(1902): note: see reference to function template instantiation 'std::_Compressed_pair<Enums::AgentType (__cdecl ILattice::* )(int),std::tuple<std::unique_ptr<ILattice,std::default_delete<_Ty>>,std::_Ph<1>>,false>::_Compressed_pair<Enums::AgentType(__cdecl ILattice::* )(int),_Cv_TiD&,const std::_Ph<1>&>(std::_One_then_variadic_args_t,_Other1 &&,_Cv_TiD &,const std::_Ph<1> &)' being compiled
1> with
1> [
1> _Ty=ILattice,
1> _Cv_TiD=std::unique_ptr<ILattice,std::default_delete<ILattice>>,
1> _Other1=Enums::AgentType (__cdecl ILattice::* )(int)
1> ]
1>d:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.14.26428\include\functional(1903): note: see reference to function template instantiation 'std::_Compressed_pair<Enums::AgentType (__cdecl ILattice::* )(int),std::tuple<std::unique_ptr<ILattice,std::default_delete<_Ty>>,std::_Ph<1>>,false>::_Compressed_pair<Enums::AgentType(__cdecl ILattice::* )(int),_Cv_TiD&,const std::_Ph<1>&>(std::_One_then_variadic_args_t,_Other1 &&,_Cv_TiD &,const std::_Ph<1> &)' being compiled
1> with
1> [
1> _Ty=ILattice,
1> _Cv_TiD=std::unique_ptr<ILattice,std::default_delete<ILattice>>,
1> _Other1=Enums::AgentType (__cdecl ILattice::* )(int)
1> ]
1>d:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.14.26428\include\functional(1902): note: while compiling class template member function 'std::_Binder<std::_Unforced,Enums::AgentType (__cdecl ILattice::* )(int),std::unique_ptr<ILattice,std::default_delete<_Ty>> &,const std::_Ph<1> &>::_Binder(_Fx &&,std::unique_ptr<_Ty,std::default_delete<_Ty>> &,const std::_Ph<1> &)'
1> with
1> [
1> _Ty=ILattice,
1> _Fx=Enums::AgentType (__cdecl ILattice::* )(int)
1> ]
1>d:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.14.26428\include\functional(1929): note: see reference to function template instantiation 'std::_Binder<std::_Unforced,Enums::AgentType (__cdecl ILattice::* )(int),std::unique_ptr<ILattice,std::default_delete<_Ty>> &,const std::_Ph<1> &>::_Binder(_Fx &&,std::unique_ptr<_Ty,std::default_delete<_Ty>> &,const std::_Ph<1> &)' being compiled
1> with
1> [
1> _Ty=ILattice,
1> _Fx=Enums::AgentType (__cdecl ILattice::* )(int)
1> ]
1>d:\predator-prey\predator-prey\main.cpp(16): note: see reference to class template instantiation 'std::_Binder<std::_Unforced,Enums::AgentType (__cdecl ILattice::* )(int),std::unique_ptr<ILattice,std::default_delete<_Ty>> &,const std::_Ph<1> &>' being compiled
1> with
1> [
1> _Ty=ILattice
1> ]
But when I am using shared_ptr instead of unique_ptr, everything works fine. Is a good practice? I've heard to avoid shared_ptr as much as I can, unless they are totally necessary.
Do you need shared_ptr?
No. At least not for the given example.
If lattice and gui are defined in different scopes with different lifespans and being used all over the place, wowie-zowie, we can talk about shared_ptr.
Why?
Let's start with a really simple example that shows why unique_ptr is causing grief.
#include <functional>
#include <iostream>
struct test
{
test() = default;
test(const test &)
{
std::cout << "copied" << std::endl;
}
void func(int i)
{
std::cout << i << std::endl;
}
};
int main()
{
test t;
std::function<void(int)> f1 = std::bind(&test::func, t, std::placeholders::_1);
f1(1);
}
test doesn't do much other than tell us when the object is copied and prove that the function ran. Executing it we'll see that t is copied and produced the expected output from the function.
std::unique_ptr cannot be copied because that would pretty much ruin the whole unique part of the job description. We see that if we change main a little to use a unique_ptr and get a little closer to the question as posed.
int main()
{
std::unique_ptr<test> tp = std::make_unique<test>();
std::function<void(int)> f1 = std::bind(&test::func, tp, std::placeholders::_1);
}
As expected, this doesn't compile. We can make this compile by using a std::reference_wrapper
std::function<void(int)> f1 = std::bind(&test::func, std::reference_wrapper<std::unique_ptr<test>>(tp), std::placeholders::_1);
or provide a raw pointer to bind
std::function<void(int)> f1 = std::bind(&test::func, tp.get(), std::placeholders::_1); f1(1);
but this requires tp to have wider scope and be guaranteed to outlive f1. What this really comes down to is why use more than test t; in the first place? Do we really need a pointer here at all?
But let's go with it for now because we can at least make this look a lot prettier before we head off to greener pastures. Here is the same thing with a lambda expression
std::function<void(int)> f1 = [&tp](int i) { tp->func(i); };
Normally I'm not a champion of "Lambda is easier to read than bind," but this case is a pretty convincing argument.
Getting back to basics, it's not really all that different from
int main()
{
test t;
std::function<void(int)> f1 = [&t](int i) { t.func(i); };
f1(1);
}
and completely eliminates the pointer. No pointer, no shared_ptr.
If t can be fire and forget, the only user is the callback, let the lambda carry around a copy of t and let the original die.
std::function<void(int)> scopedemo()
{
test t;
return [t](int i) mutable { t.func(i); }; //
}
int main()
{
auto f1 = scopedemo();
f1(1);
}
Note the mutable. Lambda defaults to carrying around constants and can't be used to invoke non-const methods or be used as a non-const parameter.
You are passing std::unique_ptr by value and it's always a bad idea, because it doesn't have a copy constructor.
Why doesn't your code even compile? It seems that passing std::unique_ptr to a bound function is a long-living bug of visual studio.
Related
This question already has answers here:
problem sorting using member function as comparator
(9 answers)
Closed 7 years ago.
Im trying to sort vector of structs. I saw this and this examples.
this is my code:
.h file:
// I didn't mention all the includes and namespace
class fileLoader
{
struct commands
{
int time;
string name;
};
commands resultStruct;
vector<commands> resultVector
private:
void sortFunction();
bool compareByTime(const commands &a, const commands &b);
}
.cpp file:
void fileLoader::sortResults()
{
sort(resultVector.begin(), resultVector.end(), compareByTime);
}
bool fileLoader::compareByTime(const commands &a, const commands &b)
{
return a.time < b.time;
}
this is the compilation error I get:
error C3867: 'fileLoader::compareByTime': function call missing argument list; use '&fileLoader::compareByTime' to create a pointer to member
error C2780: 'void std::sort(_RanIt,_RanIt)' : expects 2 arguments - 3 provided
c:\program files (x86)\microsoft visual studio 10.0\vc\include\algorithm(3639) : see declaration of 'std::sort'
when I tried to change compareByTime to &fileLoader::compareByTime, I got this compilation error:
error C2064: term does not evaluate to a function taking 2 arguments
1> c:\program files (x86)\microsoft visual studio 10.0\vc\include\algorithm(3776) : see reference to function template instantiation 'std::pair<_Ty1,_Ty2> std::_Unguarded_partition<_RanIt,_Pr>(_RanIt,_RanIt,_Pr)' being compiled
1> with
1> [
1> _Ty1=fileLoader::commands *,
1> _Ty2=fileLoader::commands *,
1> _RanIt=fileLoader::commands *,
1> _Pr=bool (__thiscall fileLoader::* )(const fileLoader::commands &,const fileLoader::commands &)
1> ]
1> c:\program files (x86)\microsoft visual studio 10.0\vc\include\algorithm(3806) : see reference to function template instantiation 'void std::_Sort<fileLoader::commands*,__w64 int,_Pr>(_RanIt,_RanIt,_Diff,_Pr)' being compiled
1> with
1> [
1> _Pr=bool (__thiscall fileLoader::* )(const fileLoader::commands &,const fileLoader::commands &),
1> _RanIt=fileLoader::commands *,
1> _Diff=__w64 int
1> ]
std::sort<std::_Vector_iterator<_Myvec>,bool(__thiscall fileLoader::* )(const fileLoader::commands &,const fileLoader::commands &)>(_RanIt,_RanIt,_Pr)' being compiled
1> with
1> [
1> _Myvec=std::_Vector_val<fileLoader::commands,std::allocator<fileLoader::commands>>,
1> _RanIt=std::_Vector_iterator<std::_Vector_val<fileLoader::commands,std::allocator<fileLoader::commands>>>,
1> _Pr=bool (__thiscall fileLoader::* )(const fileLoader::commands &,const fileLoader::commands &)
1> ]
1>c:\program files (x86)\microsoft visual studio 10.0\vc\include\algorithm(3720): fatal error C1903: unable to recover from previous error(s); stopping compilation
1>
1>Build FAILED.
I will be happy for some help.
thanks.
First fix all the typos (missing semicolons etc).
Then change your sorting function to static bool compareByTime(const commands &a, const commands &b);
Short answer
Make compareByTime static (or 'global' outside class)
Explanation
Member functions require this to be passed to them somehow, so two argument member function is probably 3 argument function. So compiler can't use it when it needs 2 arguments comparator.
i have map that looks like this:
typedef std::map<PuzzlePartLocation, std::shared_ptr<PuzzleObj>> ComponentsMap;
now i try to set this map with elements via fucntion like this:
void ComponentMadiator::Register(const PuzzlePartLocation puzzlePartLocation,PuzzleObj* puzzleObj)
{
componentsMap[puzzlePartLocation] = std::make_shared<PuzzleObj>(puzzleObj);
}
i just call it like this :
PuzzleObj* pPuzzleObjStickLeft = new PuzzleObj()
pComponentMadiator->Register(1,pPuzzleObjStickLeft );
PuzzleObj contains memeber from type IImageComponent *
PuzzleObj inherits from base class
but it give me error like this :
1>c:\program files\microsoft visual studio 11.0\vc\include\memory(873): error C2664: 'PuzzleObj::PuzzleObj(IImageComponent *)' : cannot convert parameter 1 from 'PuzzleObj *' to 'IImageComponent *'
1> Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
1> c:\program files\microsoft visual studio 11.0\vc\include\memory(972) : see reference to function template instantiation 'std::_Ref_count_obj<_Ty>::_Ref_count_obj<PuzzleObj*&>(_V0_t)' being compiled
1> with
1> [
1> _Ty=PuzzleObj,
1> _V0_t=PuzzleObj *&
1> ]
1> c:\program files\microsoft visual studio 11.0\vc\include\memory(972) : see reference to function template instantiation 'std::_Ref_count_obj<_Ty>::_Ref_count_obj<PuzzleObj*&>(_V0_t)' being compiled
1> with
1> [
1> _Ty=PuzzleObj,
1> _V0_t=PuzzleObj *&
1> ]
1> d:\dev\cpp\cocos2d-x\cocos2d-x-3.0beta2\cocos2d-x-3.0beta2\projects\neonbreaker\classes\componentmadiator.cpp(23) : see reference to function template instantiation 'std::shared_ptr<_Ty> std::make_shared<PuzzleObj,PuzzleObj*&>(_V0_t)' being compiled
1> with
1> [
1> _Ty=PuzzleObj,
1> _V0_t=PuzzleObj *&
1> ]
std::make_shared<PuzzleObj> creates a new PuzzleObj for you. What you need is std::shared_ptr<PuzzleObj>(puzzleObj).
More importantly
void ComponentMadiator::Register(const PuzzlePartLocation puzzlePartLocation,PuzzleObj* puzzleObj);
should be declared as:
void ComponentMadiator::Register(const PuzzlePartLocation puzzlePartLocation, std::shared_ptr<PuzzleObj> const& puzzleObj);
because it shares ownership of puzzleObj by storing it in the container and that must be communicated in function's interface.
And call it like so:
std::shared_ptr<PuzzleObj> pPuzzleObjStickLeft(std::make_shared<PuzzleObj>());
pComponentMadiator->Register(1, pPuzzleObjStickLeft);
I'd like to capture a variable of type std::vector<std::unique_ptr<MyClass>> in a lambda expression (in other words, "capture by move"). I found a solution which uses std::bind to capture unique_ptr (https://stackoverflow.com/a/12744730/2478832) and decided to use it as a starting point. However, the most simplified version of the proposed code I could get doesn't compile (lots of template mistakes, it seems to try to call unique_ptr's copy constructor).
#include <functional>
#include <memory>
std::function<void ()> a(std::unique_ptr<int>&& param)
{
return std::bind( [] (int* p) {},
std::move(param));
}
int main()
{
a(std::unique_ptr<int>(new int()));
}
Can anybody point out what is wrong with this code?
EDIT: tried changing the lambda to take a reference to unique_ptr, it still doesn't compile.
#include <functional>
#include <memory>
std::function<void ()> a(std::unique_ptr<int>&& param)
{
return std::bind( [] (std::unique_ptr<int>& p) {}, // also as a const reference
std::move(param));
}
int main()
{
a(std::unique_ptr<int>(new int()));
}
Here's Visual Studio 2012 output:
1>C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\include\tuple(151): error C2248: 'std::unique_ptr<int,std::default_delete<_Ty>>::unique_ptr' : cannot access private member declared in class 'std::unique_ptr<int,std::default_delete<_Ty>>'
1> with
1> [
1> _Ty=int
1> ]
1> C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\include\memory(1447) : see declaration of 'std::unique_ptr<int,std::default_delete<_Ty>>::unique_ptr'
1> with
1> [
1> _Ty=int
1> ]
1> C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\include\tuple(521) : see reference to function template instantiation 'std::_Tuple_val<_This>::_Tuple_val<const _Ty&>(_Other)' being compiled
1> with
1> [
1> _This=std::unique_ptr<int,std::default_delete<int>>
1> , _Ty=std::unique_ptr<int,std::default_delete<int>>
1> , _Other=const std::unique_ptr<int,std::default_delete<int>> &
1> ]
1> C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\include\tuple(521) : see reference to function template instantiation 'std::_Tuple_val<_This>::_Tuple_val<const _Ty&>(_Other)' being compiled
1> with
1> [
1> _This=std::unique_ptr<int,std::default_delete<int>>
1> , _Ty=std::unique_ptr<int,std::default_delete<int>>
1> , _Other=const std::unique_ptr<int,std::default_delete<int>> &
1> ]
1> C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\include\tuple(521) : while compiling class template member function 'std::tuple<std::unique_ptr<int,std::default_delete<_Ty>>,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil>::tuple(const std::tuple<std::unique_ptr<_Ty,std::default_delete<_Ty>>,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil> &)'
1> with
1> [
1> _Ty=int
1> ]
1> C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\include\functional(1152) : see reference to function template instantiation 'std::tuple<std::unique_ptr<int,std::default_delete<_Ty>>,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil>::tuple(const std::tuple<std::unique_ptr<_Ty,std::default_delete<_Ty>>,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil> &)' being compiled
1> with
1> [
1> _Ty=int
1> ]
1> C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\include\functional(1152) : see reference to class template instantiation 'std::tuple<std::unique_ptr<int,std::default_delete<_Ty>>,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil>' being compiled
1> with
1> [
1> _Ty=int
1> ]
1> main.cpp(15) : see reference to class template instantiation 'std::_Bind<false,void,a::<lambda_2ad08ede4c4ce9c02d5497417b633d1d>,std::unique_ptr<int,std::default_delete<_Ty>>,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil>' being compiled
1> with
1> [
1> _Ty=int
1> ]
The second argument to bind will be passed to the bound object at the time of call. The problem is that the lambda takes a int*, but the argument is a std::unique_ptr<int> and there is no conversion from the latter to the former.
It should compile (untested) if you change the signature of the lambda to take a std::unique_ptr by reference/const-reference
The problem with the version that passes a lambda taking unique_ptr by reference to std::bind is your conversion to std::function - std::function requires functions to be CopyConstructible ([func.wrap.func.con] p7). Try it without the std::function (Live at ideone):
auto f = std::bind([](std::unique_ptr<int>&){},
std::make_unique<int>());
My understanding of the internals of std::bind is that it will always make a copy of the 1st argument object that is being bound to the function-object rather than moving it (even if that argument is an rvalue), so you'll always end up with a call to the copy-constructor for whatever object you are attempting to bind to the function object, and not the move-constructor, even with the use of std::move.
Edit:
This has been reported as a VS2012 C++ compiler bug on Microsoft Connect (link).
Nov. 11, 2014: Microsoft has responded saying the fix for this bug should show up in the next major release of Visual C++.
I've been struggling with a VS2012 compiler error message I don't understand, so I trimmed down the problem to what seems like the bare minimum.
I'm building the following main.cpp using VS2012:
#include <utility>
template <typename T>
struct A
{
T x;
A(A&& other) : x(std::move(other.x)) { }
A(T&& x) : x(std::move(x)) { }
};
template <typename T>
A<T> build(T&& x)
{
return A<T>(std::move(x));
}
int main(int argc, char* argv[])
{
auto f = []()
{
return build([](){}); //error here
};
return 0;
}
The salient point is that I'm trying to use a lambda as the template type T of the build function. The error message I get is:
1> main.cpp
1>C:\test\main.cpp(21): error C2664: 'A<T>::A(A<T> &&)' : cannot convert parameter 1 from 'A<T>' to 'A<T> &&'
1> with
1> [
1> T=void (__cdecl *)(void)
1> ]
1> and
1> [
1> T=main::<lambda_c3c618d445b3cb24eede9bf304860ad7>::()::<lambda_4240e93016e3e420ff8383c9350ae130>
1> ]
1> and
1> [
1> T=void (__cdecl *)(void)
1> ]
1> Reason: cannot convert from 'A<T>' to 'A<T>'
1> with
1> [
1> T=main::<lambda_c3c618d445b3cb24eede9bf304860ad7>::()::<lambda_4240e93016e3e420ff8383c9350ae130>
1> ]
1> and
1> [
1> T=void (__cdecl *)(void)
1> ]
1> No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
I've done my research and looked up the page for the error message (link), but I still can't figure out what the problem is. Could you please explain this compiler error?
edit
Something is definitely weird here. If I change the code in main to look like this:
auto f = []()
{
int* n = new int(0);
auto g = [=](){ return *n; };
*n++;
return build<decltype(g)>(std::move(g));
};
I get an error message suggesting that T=int (__cdecl *)(void) in the call to build - which would mean that decltype(g) is giving me a function pointer? Huh? I'm capturing a pointer by value and then modifying it afterwards - shouldn't it have to create a functor - and one that has no cast to function pointer? Maybe I'm not understanding something.
See related: Lambda expressions : n3290 draft
Also, if this is a bug in the VS2012 compiler, can you think of a workaround?
I can confirm that using GCC (on linux), this code compiles just fine.
So I'd say that VisualStudio seems to be the source of the error.
I don't have Windows or Visual Studio to verify, nor do I have much experience with lambda functions in C++, but perhaps you need to include the (albeit empty) parameter list in the function? i.e. change line 21 to
return build([](){});
Both versions compile with GCC, but perhaps Visual Studio is a bit more picky.
The other question I might have is whether the lambda function you're defining at line 24 will work out since its return value involves the lambda function you're defining inside the function itself.
I do not know if that behavior comply with the standard but with VC++ 2019 that error happen only with the option /permissive-, then when the strict mode is on.
Nevertheless here is how to solve the problem, by just casting the lambda with a reference type:
template <typename FUNC>
void f(FUNC& o){}
int main()
{
f((std::function<void()>&)[](){});
// or also:
auto func = [](){};
f(func);
}
This code supposedly works with GCC - I am trying to get it to work with Visual Studio. I can't figure out if the code is actually faulty or I'm not doing something right with the port.
1>c:\somepath\aaa.h(52): error C2101: '&' on constant
1> c:\somepath\aaa.h(52): while compiling class template member function 'const blahblah::Message something::AClass<Type>::aMethod(void) const'
1> with
1> [
1> Type=const lala::BClass&
1> ]
1> c:\somepath\bbb.h(79) : see reference to class template instantiation 'something:AClass<Type>' being compiled
1> with
1> [
1> Type=const lala::BClass&
1> ]
1> MyApplication.cpp
Files
aaa.h:52 virtual const Type aMethod() const { return Type(); }
bbb.h:79 AClass<const BClass&> blahblahblah_;
Constructing T() where T is a reference type is not valid, and makes no sense. Some versions of gcc incorrectly accept it.