Why is "TArgs" ambiguous in my Example?
The compiler should know the only existing function signature
virtual CGame::NetCreatePlayer(CNetPeer* _pPeer, int _ObjectID, const CNetMessage& _ObjectData, bool _bAuthority);
and deduce correct TArgs in this function:
template<typename... TArgs >
void INetInterface<TSubClass>::NetCall(void(TSubClass::*_pFunc)(CNetPeer*, TArgs...), CNetPeer* _pPeer, TArgs... _Params);
I want to call it like this:
CNetMessage obj;
//...
NetCall(&CGame_Server::NetCreatePlayer, _pClient, 0, obj, true);
CGame_Server inherits CGame.
Compiler output:
4> error C2782: 'void INetInterface<CGame>::NetCall(void (__thiscall CGame::* )(CNetPeer *,TArgs...),CNetPeer *,TArgs...)'
: template parameter 'TArgs' is ambiguous
4> NetInterface.h(82) : see declaration of INetInterface<CGame>::NetCall'
4> could be 'int, const CNetMessage&, bool'
4> or 'int, CNetMessage, bool'
It can't be 'int, CNetMessage, bool', right?
Is there a way to get around this problem?
I tried casting to const CNetMessage& but strangely enough that does not help.
And no there are no other member functions with that same name.
NetCall(&CGame_Server::NetCreatePlayer, _pClient, 0, obj, true);
There are two places in this call from which TArgs can be deduced:
From the member function pointer's type, the compiler deduces TArgs == int, const CNetMessage&, bool
From the parameters 0, obj, true, the compiler deduces TArgs == int, CNetMessage, bool
The results conflict. To fix this, use the identity trick to put the second TArgs into a non-deduced context:
template<class T> struct identity { using type = T; };
template<class T> using identity_t = typename identity<T>::type;
template<typename... TArgs >
void INetInterface<TSubClass>::NetCall(void(TSubClass::*_pFunc)(CNetPeer*, TArgs...),
CNetPeer* _pPeer, identity_t<TArgs>... params);
As a side note, _Params is a reserved identifier, along with _ObjectData and _ObjectID; you should rename them.
Related
I have the following templated method:
auto clusters = std::vector<std::pair<std::vector<long>, math::Vector3f>>
template<class T>
void eraserFunction(std::vector<T>& array, std::function<int(const T&, const T&)> func)
{
}
And I have a function that looks like
auto comp1 = [&](
const std::pair<std::vector<long>, math::Vector3f>& n1,
const std::pair<std::vector<long>, math::Vector3f>& n2
) -> int {
return 0;
};
math::eraserFunction(clusters, comp1);
However, I get a syntax error saying:
116 | void eraserFunction(std::vector<T>& array, std::function<int(const T&, const T&)> func)
| ^~~~~~~~~~~~~~
core.hpp:116:6: note: template argument deduction/substitution failed:
geom.cpp:593:23: note: 'math::method(const at::Tensor&, const at::Tensor&, int, float, int, int, float)::<lambda(const std::pair<std::vector<long int>, Eigen::Matrix<float, 3, 1> >&, const std::pair<std::vector<long int>, Eigen::Matrix<float, 3, 1> >&)>' is not derived from 'std::function<int(const T&, const T&)>'
593 | math::eraserFunction(clusters, comp1);
The function call tries to deduce T from both the first and second function parameter.
It will correctly deduce T from the first parameter, but fail to deduce it from the second parameter, because the second function argument is a lambda type, not a std::function type.
If deduction isn't possible from all parameters that are deduced context, deduction fails.
You don't really need deduction from the second parameter/argument here, since T should be fully determined by the first argument. So you can make the second parameter a non-deduced context, for example by using std::type_identity:
void eraserFunction(std::vector<T>& array, std::type_identity_t<std::function<int(const T&, const T&)>> func)
This requires C++20, but can be implemented easily in user code as well if you are limited to C++11:
template<typename T>
struct type_identity { using type = T; };
and then
void eraserFunction(std::vector<T>& array, typename type_identity<std::function<int(const T&, const T&)>>::type func)
std::identity_type_t<T> is a type alias for std::identity_type<T>::type. Everything left to the scope resolution operator :: is a non-deduced context, which is why that works.
If you don't have any particular reason to use std::function here, you can also just take any callable type as second template argument:
template<class T, class F>
void eraserFunction(std::vector<T>& array, F func)
This can be called with a lambda, function pointer, std::function, etc. as argument. If the argument is not callable with the expected types, it will cause an error on instantiation of the function body containing the call. You can use SFINAE or since C++20 a type constraint to enforce this already at overload resolution time.
Check the below code.
#include <future>
template <class F, class... Args>
void do_something(F f, Args... args) {
using return_type = typename std::result_of<F(Args...)>::type;
// Why below gives an error?
std::packaged_task<return_type(Args...)> task(f, args...);
}
int func(int a, int b) {
}
int main() {
do_something(func, 1, 2);
}
The packaged_task constructor gives a following error.
error: no matching function for call to 'std::packaged_task<int(int, int)>::packaged_task(int (*&)(int, int), int&, int&)'
8 | std::packaged_task<return_type(Args...)> task(f, args...);
The thing I don't understand is that why f and args became a reference type in the constructor? The Args... were int, int types whereas args... just became int&, int&. Where is this coming from?
packaged_task do not have the signature you want.
compiler is saying there is no such function. (probably with candidates below it)
I have two template method
template <typename T, typename Ret, typename ...Args>
Ret apply(T* object, Ret(T::*method)(Args...), Args&& ...args) {
return (object->*method)(std::forward(args)...);
};
template <typename T, typename Ret, typename ...Args>
Ret apply(T* object, Ret(T::*method)(Args...) const, Args&& ...args) {
return (object->*method)(std::forward(args)...);
};
My purpose is apply member method of class T on these args
this is my test code:
int main() {
using map_type = std::map<std::string, int>;
map_type map;
map.insert(std::make_pair("a", 1));
std::cout << "Map size: " << apply(&map, &map_type::size) << std::endl; //this code work
apply(&map, &map_type::insert, std::make_pair("a", 1)); //failed to compile
return 0;
}
This is compiler error message:
test.cpp: In function ‘int main()’:
test.cpp:61:58: error: no matching function for call to ‘apply(map_type*, <unresolved overloaded function type>, std::pair<const char*, int>)’
apply(&map, &map_type::insert, std::make_pair("a", 1));
^
test.cpp:11:5: note: candidate: template<class T, class Ret, class ... Args> Ret apply(T*, Ret (T::*)(Args ...), Args&& ...)
Ret apply(T* object, Ret(T::*method)(Args...), Args&& ...args) {
^~~~~
test.cpp:11:5: note: template argument deduction/substitution failed:
test.cpp:61:58: note: couldn't deduce template parameter ‘Ret’
apply(&map, &map_type::insert, std::make_pair("a", 1));
std::map::insert is an overloaded function. You cannot take its address unless you explicitly specify the overload you're interested about - how else would the compiler know?
The easiest way to solve your problem is to have apply accept an arbitrary function object and wrap your call to insert in a generic lambda.
template <typename F, typename ...Args>
decltype(auto) apply(F f, Args&& ...args) {
return f(std::forward<Args>(args)...);
};
Usage:
::apply([&](auto&&... xs) -> decltype(auto)
{
return map.insert(std::forward<decltype(xs)>(xs)...);
}, std::make_pair("a", 1));
live wandbox example
The additional syntactic boilerplate is unfortunately impossible to avoid. This might change in the future, see:
N3617 aimed to solve this issue by introducing a "lift" operator.
P0119 by A. Sutton solves the problem in a different way by allowing overload sets to basically generate the "wrapper lambda" for you when passed as arguments.
I'm not sure if overloaded member functions are supported in the above proposals though.
You can alternatively use your original solution by explicitly specifying the overload you're intersted in on the caller side:
::apply<map_type, std::pair<typename map_type::iterator, bool>,
std::pair<const char* const, int>>(
&map, &map_type::insert<std::pair<const char* const, int>>,
std::make_pair("a", 1));
As you can see it's not very pretty. It can be probably improved with some better template argument deduction, but not by much.
I build with VS2013 compiler these member functions:
template<typename R, typename ...Args>
fc::variant memberfoo( const std::function<R(Args...)>& f, variants::const_iterator a0, variants::const_iterator e )const
{
return fc::variant(f(Args...)); // error C2440: '<function-style-cast>' : cannot convert from 'void' to 'fc::variant'
}
template<typename ...Args>
fc::variant memberfoo(const std::function<void(Args...)>& f, variants::const_iterator a0, variants::const_iterator e)const
{
f(Args... );
return fc::variant();
}
I think that reason of error is that compiler does not chose member by current template type of parameter "std::function< void(Args...) > &" and in this case he select first memberfoo.
How to help compiler to chose member by type of std::function< some_function_declaration > ? Or some other tip how to adjust code to be compilable ?
I would like to pass a template method as a template argument.
I don't understand why I am getting this error:
no known conversion for argument 1 from '<unresolved overloaded function type>' to 'void (B::*&&)(int&&, double&&)
Here is the code:
struct A {
template <class Fn, class... Args>
void f(Fn&& g, Args&&... args) {
g(std::forward<Args>(args)...);
}
};
struct B {
template <class... Args>
void doSomething(Args&&... args) {}
void run() {
A a;
a.f(&doSomething<int, double>, 1, 42.); // error here
}
};
int main() {
B b;
b.run();
return 0;
}
Any ideas?
The root cause for the error is that you need an object to call the member function. However, with current code, the error is not that straightforward.
Change calling site to
a.f(&B::doSomething<int, double>, 1, 42.)
And you will see much better error:
error: must use '.' or '->' to call pointer-to-member function in 'g
(...)', e.g. '(... ->* g) (...)'
doSomething is a member function, as such, it cannot be called without an object, which you are trying to do
g(std::forward<Args>(args)...);
^
where is the instance?
One solution to this is to wrap doSomething in a lambda:
a.f([](B& instance, int a, int b) { instance.doSomething(a, b); }, *this, 1, 42.);
If you can use C++17, you could also use std::invoke:
template <class Fn, class... Args>
void f(Fn&& g, Args&&... args) {
std::invoke(g, std::forward<Args>(args)...);
}
and then when calling f:
a.f(&B::doSomething<int, double>, this, 1, 42.);