Here is a code snippet:
#include <functional>
#include <iostream>
#include <memory>
template <typename T>
using Callback = std::function<void(const T)>;
template <typename T>
void Call(const T yyy, const Callback<T>& callback) {
callback(yyy);
}
template <typename T>
class MyCallback {
public:
explicit MyCallback(const T counter_init) : counter_{counter_init} {}
void operator ()(const T xxx) {
std::cout << counter_ << '\t' << xxx << std::endl;
++counter_;
}
private:
T counter_;
};
int main() {
const auto callback = std::make_unique<MyCallback<int>>(0);
Call(111, *callback); // expected output is "0 \t 111"
Call(222, *callback); // expected output is "1 \t 222"
return 0;
}
Clang says it couldn't match std::function against MyCallback and g++ is thinks that MyCallback is derived from Callback.
clang++ -std=c++14 main.cpp && ./a.out
g++ -std=c++14 main.cpp && ./a.out
The easiest way I know that will fix this issue is to use template instead of Callback, so that Call will be defined in the following manner:
template <typename T, typename F>
void Call(const T yyy, F&& callback) {
callback(yyy);
}
But it would be unclear for the next developer which signature does callback has.
Can some one clarify what is going on in the first example from compiler point of view and how can I fix this without applying hack I described above?
Change Call to:
template <class T, class F,
class R=typename std::result_of<F&(const T&)>::type
>
void Call(const T& yyy, F&& f) {
f(yyy);
}
now we invoke f on yyy, and (assuming you have a C++11 compiler that implements SFINAE safe result_of) the overload only works if you can call f on yyy.
std::function is not a generic callable. It is a way to type-erase callables down to "being called with a given signature", copy the callable, and destroy the callable.
Type-erasure and type-deduction are opposite operations. Doing both in one step is usually a sign of a design flaw. std::function should only extremely rarely be deduced from the signature of a passed-in callable.
Instead, determine how you are going to use a given function. Then type-erase down to that use signature, or just test against that use signature and don't type-erase at all.
If you have multiple possible use-signatures, test against each of them, tag dispatch to the right type-erasure path, and type erase there.
The result_of clause is optional, but it improves error messages significantly. It also makes the error detectible "early" as a failed overload, instead of as a hard error. The result_of clause can be converted to a static_assert in the body that would generate an even clearer error message, but that would fail "late" after overload resolution.
An alternative approach, that blocks deduction on the function, is:
template<class T>struct tag{using type=T;};
template<class Tag>using type_t=typename Tag::type;
template<class T>using block_deduction=type_t<tag<T>>;
template <typename T>
using Callback = block_deduction<std::function<void(const T)>>;
and now
template <class T>
void Call(const T& yyy, const Callback<T>& callback) {
callback(yyy);
}
works. It still erases the callback type needlessly (with the resulting overhead).
Deduction is blocked by round-tripping through a struct and getting the ::type. Under the standard, such dependent types are never deduced.
std::function<void(const T)> can be value-constructed with *callback. The problem is Call is a function template. The compiler is unable to automatically instantiate void Call(const int yyy, const Callback<int>& callback) from Call(111, *callback);, since it does not match the call. Conversion is necessary to call the instantiation with Call(111, *callback);. But automatic template argument deduction does not consider such conversions. It considers exact match only. You could manually instantiate it though. So, one way to correct your code would be changing
Call(111, *callback); // expected output is "0 \t 111"
Call(222, *callback); // expected output is "1 \t 222"
to
Call<int>(111, *callback); // expected output is "0 \t 111"
Call<int>(222, *callback); // expected output is "1 \t 222"
As such, a temporary std::function<void(const T)> if first value-constructed with *callback. The function parameter const Callback<int>& callback in Call is then bound to this temporary. Again, this is all about conversion. The function signature is not an exact match for the call.
From [temp.arg.explicit]:
Implicit conversions (Clause 4) will be performed on a function argument to convert it to the type of the
corresponding function parameter if the parameter type contains no template-parameters that participate
in template argument deduction. [ Note: Template parameters do not participate in template argument
deduction if they are explicitly specified. For example,
template<class T> void f(T);
class Complex {
Complex(double);
};
void g() {
f<Complex>(1); // OK, means f<Complex>(Complex(1))
}
—end note ]
In your case, const Callback<T>& does contain a template-parameter that participates in template argument deduction, so no implicit conversion (i.e. MyCallback<int> to std::function<void(const int)>) is allowed.
In order to use std::function, you'd have to have the callback argument not participate in any template argument deduction. That is:
Call<int>(111, *callback); // no argument deduction done here
Alternatively, you could just deduce callback itself, no type-erasure necessary:
template <typename T, typename F>
void Call(const T yyy, F&& callback) {
callback(yyy);
}
Lastly, if you really want the type erasure, you could manually construct your Callback internally to Call:
template <typename T, typename F>
void Call(const T yyy, F&& f) {
Callback<T> callback(std::forward<F>(f));
callback(yyy);
}
Related
Currently using g++11.3.0, C++20.
I'm trying to compile this bit of code below, where the function foo() is supposed to be able to take an arbitrary number of template arguments, and use them to provide arguments when calling the passed lambda func. I wanted to create a Callable concept that restricts func to only invocables. The code compiles fine as it is. But when I change the line template <typename ...T, typename Func> to template <typename ...T, Callable Func> to use the Callable concept, I get the following error:
error: no matching function for call to ‘foo<int, float, double>(main()::<lambda(int&&, float&&, double&&)>)’
Code:
#include <functional>
template <typename Func, typename ...Args>
concept Callable = requires (Func&& func, Args&&... args)
{
std::invoke(std::forward<Func>(func), std::forward<Args>(args)...);
};
template <typename ...T, typename Func>
void foo(Func&& func)
{
// T() is used just to show that each template argument is used to provide some sort
// of an argument for the lambda.
func(T()...);
}
int main()
{
foo<int, float, double>([] (int&&, float&&, double&&) {});
return 0;
}
Declaring Callable Func will cause the compiler will only check whether Callable<F> is true, that is, whether F can be invoked without arguments.
You need to pass the argument types to Callable to constrain the single Func template parameter, for example
template <typename ...T, Callable<T...> Func>
void foo(Func&& func)
{
// T() is used just to show that each template argument is used to provide some sort
// of an argument for the lambda.
func(T()...);
}
I want to pass a callable (std::function object) into a class Foo. The callable refers to a member method of another class which has arbitrary arguments, hence the Foo must be a variadic template. Consider this code:
struct Bar {
void MemberFunction(int x) {}
};
template<typename ...Args>
class Foo {
public:
Foo(std::function<void(Bar*, Args...)> f) {}
};
int main() {
Foo<int> m1(&Bar::MemberFunction);
return 0;
}
This compiles fine. Now I want to write a factory function MakeFoo() which returns a unique_ptr to a Foo object:
template<typename ...Args>
std::unique_ptr<Foo<Args...>> MakeFoo(std::function<void(Bar*, Args...)> f) {
return std::make_unique<Foo<Args...>>(f);
}
Using this function by calling
auto m2 = MakeFoo<int>(&Bar::MemberFunction);
in main, gives me the following compiler errors:
functional.cc: In function ‘int main()’:
functional.cc:21:50: error: no matching function for call to ‘MakeFoo(void (Bar::*)(int))’
auto m2 = MakeFoo<int>(&Bar::MemberFunction);
^
functional.cc:15:35: note: candidate: template<class ... Args> std::unique_ptr<Foo<Args ...> > MakeFoo(std::function<void(Bar*, Args ...)>)
std::unique_ptr<Foo<Args...>> MakeFoo(std::function<void(Bar*, Args...)> f) {
^
functional.cc:15:35: note: template argument deduction/substitution failed:
functional.cc:21:50: note: mismatched types ‘std::function<void(Bar*, Args ...)>’ and ‘void (Bar::*)(int)’
auto m2 = MakeFoo<int>(&Bar::MemberFunction);
It seems to me, that when I call the constructor of Foo, the compiler happily converts the function pointer &Bar::MemberFunction to a std::function object. But when I pass the same argument to the factory function, it complains. Moreover, this problem only seems to occur, when Foo and MakeFoo are variadic templates. For a fixed number of template parameters it works fine.
Can somebody explain this to me?
Why doesn't it work without explicit <int>?
Prior to C++17, template type deduction is pure pattern matching.
std::function<void(Foo*)> can store a member function pointer of type void(Foo::*)(), but a void(Foo::*)() is not a std::function of any kind.
MakeFoo takes its argument, and pattern matches std::function<void(Bar*, Args...)>. As its argument is not a std::function, this pattern matching fails.
In your other case, you had fixed Args..., and all it had to do was convert to a std::function<void(Bar*, Args...)>. And there is no problem.
What can be converted to is different than what can be deduced. There are a myriad of types of std::function a given member function could be converted to. For example:
struct Foo {
void set( double );
};
std::function< void(Foo*, int) > hello = &Foo::set;
std::function< void(Foo*, double) > or_this = &Foo::set;
std::function< void(Foo*, char) > why_not_this = &Foo::set;
In this case there is ambiguity; in the general case, the set of template arguments that could be used to construct some arbitrary template type from an argument requires inverting a turing-complete computation, which involves solving Halt.
Now, C++17 added deduction guides. They permit:
std::function f = &Foo::set;
and f deduces the signature for you.
In C++17, deduction doesn't guides don't kick in here; they may elsewhere, or later on.
Why doesn't it work with explicit <int>?
Because it still tries to pattern match and determine what the rest of Args... are.
If you changed MakeFoo to
template<class T>
std::unique_ptr<Foo<T>> MakeFoo(std::function<void(Bar*, T)> f) {
return std::make_unique<Foo<T>>(f);
}
suddenly your code compiles. You pass it int, there is no deduction to do, and you win.
But when you have
template<class...Args>
std::unique_ptr<Foo<Args...>> MakeFoo(std::function<void(Bar*, Args...)> f) {
return std::make_unique<Foo<T>>(f);
}
the compiler sees <int> and says "ok, so Args... starts with int. What comes next?".
And it tries to pattern match.
And it fails.
How can you fix it?
template<class T>struct tag_t{using type=T; constexpr tag_t(){}};
template<class T>using block_deduction=typename tag_t<T>::type;
template<class...Args>
std::unique_ptr<Foo<Args...>> MakeFoo(
block_deduction<std::function<void(Bar*, Args...)>> f
) {
return std::make_unique<Foo<T>>(f);
}
now I have told the compiler not to deduce using the first argument.
With nothing to deduce, it is satisfied that Args... is just int, and... it now works.
The compiler cannot deduce the template arguments for std::function from a different type, such as member function pointer. Even though a std::function can be constructed from on object of that type, to consider the constructor the template arguments of std::function must be first known.
To help it deduce, add another overload:
template<typename ...Args>
std::unique_ptr<Foo<Args...>> MakeFoo(void(Bar::*f)(Args...)) {
return std::make_unique<Foo<Args...>>(f);
}
MakeFoo<int>( whatever );
is equivalent to invoking the hypothetical
template<typename ...Tail>
std::unique_ptr<Foo<int,Tail...>> MakeFoo( std::function<void(Bar*,int,Tail...)> f) {
return std::make_unique<Foo<int,Tail...>>(f);
}
clearly, in no way the compiler can deduce that Tail is empty given a void(Bar::*)(int)
IMO, the most correct fix ( given the required usage ) is to make the args non deduced:
template< typename T >
struct nondeduced { using type = T; };
template<typename ...Args>
std::unique_ptr<Foo<Args...>> MakeFoo( std::function<void(Bar*, typename nondeduced<Args>::type... )> f ) {
This is similar to the question, but a more specific case. This time, no compiler work as expected.
template<class T>
struct nondeduced
{
using type = T;
};
template<class T>
using nondeduced_t = typename nondeduced<T>::type;
template<class... T, class U>
void f(void(*)(nondeduced_t<T>..., U)) {}
void g(int, char) { }
int main()
{
f<int>(g); // error?
}
In the above example, the parameter pack T cannot be deduced, but the compiler should be able to deduce U after explicit arguments substitution for pack T (i.e. single int in this case).
The above is expected to work without the nondeduced_t trick as well:
template<class... T, class U>
void f(void(*)(T..., U)) {}
Because the parameter pack T is already in non-deduced context according to
[temp.deduct.type]p5
The non-deduced contexts are:
A function parameter pack that does not occur at the end of the parameter-declaration-list.
Unfortunately, no compiler I tested (g++/clang) accept the code.
Notably something like below works on both g++ & clang.
template<class... T>
void f(void(*)(nondeduced_t<T>..., char)) {}
And again, this doesn't work on both:
template<class... T>
void f(void(*)(T..., char)) {}
Is my expectation wrong?
By [temp.deduct.type]p5 one of the non-deduced-context is
A function parameter pack that does not occur at the end of the parameter-declaration-list.
Parameter packs that doesn't appear as the last argument of a template functions are never deduced, but is completely right to specify the parameter types disabling the deduction. e.g
template<class T1, class ... Types> void g1(Types ..., T1);
g1<int, int, int>(1,2,3); // works by non-deduction
g1(1,2,3) // violate the rule above by non-deduced context
But changing the order of function argument even leaving the template parameters as they are, remove the non-deduced context condition and break the infinite expansion of parameter pack. e.g
template<class T1, class ... Types> void g1(T1, Types ...);
g1(1,2,3) // works because its a deduced context.
There're two reasons your code don't compile:
The order of function argument create a non-deduced-context which cause the type of the parameter pack T in the pattern stated in function f would never be deduced.
The template parameter T appears only as a qualifiers in function arguments(e.g nondeduced_t) and not directly specified as a function argument(which allow argument deduction).
To make the code compile you have either place the expansion of the parameter pack as it is forgetting the nondeduced_t indirect, as
template<class... T,class U>
void f( void(*)(U,T...) ) { }
f(g);
or changing the order of template parameters and specify the template argument on function call, as
template<class U,class... T>
void f( void(*)(U,typename nondeduced<T>::type...) ) {}
f<int,char>(g);
I opened this post about forwarding reference, this is a (hopefully) MCVE code:
#include <functional>
#include <vector>
using namespace std;
struct MultiMemoizator {
template <typename ReturnType, typename... Args>
ReturnType callFunction(std::function<ReturnType(Args...)> memFunc, Args&&... args) {
}
};
typedef vector<double> vecD;
vecD sort_vec (const vecD& vec) {
return vec;
}
int main()
{
vecD vec;
std::function<vecD(const vecD&)> sortFunc(sort_vec);
MultiMemoizator mem;
mem.callFunction<vecD, vecD>(sortFunc, vec);
}
Since this is not the whole code, maybe I'll have to add extra code based on the answers.
Anyway, as was suggested in this answer, forwarding reference is not possible with this version, since Args is not deduced.
So my question is: is it possible to make this code "forwarding referencable"?
In order to perfect-forward your arguments, you need to have the types deduced. You can do this by deducing the arguments to the function and the parameters to the functor separately:
template <typename ReturnType, typename... FunArgs, typename... Args>
ReturnType callFunction(std::function<ReturnType(FunArgs...)> memFunc,
Args&&... args)
{
//...
}
Then you can call callFunction without template parameters and have everything deduced:
mem.callFunction(sortFunc, vec);
I will add a bit of details regarding #TartanLlama answer on why your code fails to compile (even without the explicit template parameters) but also why (in my own opinion) your code is dangerous.
In the following, I will use only a simple type T instead of your parameter pack Args... because it is simpler to explain and does not change the meaning.
A bit of reminder on forwarding references...
First, let's take a simpler example than yours with simply the following:
template <typename T>
void f (T&&);
Now, let's instanciate f from various sources, let's assume with have the following variables:
std::string s;
const std::string cs;
...then:
f(s); // instanciate f<std::string&>
f(cs); // instanciate f<const std::string&>
f(std::string()); // instanciate f<std::string&&>
You should be wondering: Why is the first instanciation f<std::string&> instead of f<std::string>?, but the standard tells you (§14.8.2.1#3 [temp.deduct.call]):
If P is a forwarding reference and the argument is an
lvalue, the type “lvalue reference to A” is used in place of A for type deduction.
Back to our initial snippet!
Now, let's complicate a bit our example:
template <typename T>
struct A {};
template <typename T>
void f (A<T>, T&&);
And one instantiation:
std::string s;
A<std::string> as;
f(as, s);
The above is equivalent to your example, and will fails to compile, but why... ? Well, as explained above, when you have an lvalue, the deduced type for T&& is T&, not T, and thus the type deduction fails for A<T> because the compiler is expecting A<std::string&> and you are giving a A<std::string>.
So now we know that we have to do the following:
A<std::string&> ars;
A<std::string const&> acrs;
f(ars, s); // good
f(acrs, cs); // good
Why is it dangerous?
Ok so now, this should be good:
A<std::string&&> arrs;
f(arrs, std::string());
But it is not... Because when T is deduced as a rvalue reference, T is simply T, so the compiler is expecting A<std::string>.
So here is the problem: You are going to give a rvalue to a method that is going to forward it to a function expecting an lvalue. That's not wrong, but it is probably not what you'd have expected.
How to deal with it?
The first possibility is to force the type of the first parameter regardless of the deduced type for T, e.g.:
template <typename T>
void f (A<typename std::remove_reference<T>::type>, T&&);
But note:
You would have to add more stuff to deal with const.
One may wonder the usefulness of T&& when the type of the first argument is fixed (in your case, at least).
The second possibility (warning: I don't know if this is standard!) is to move the first parameter at the end and then deduce the type from t:
template <typename T>
void f (T &&t, A<decltype(std::forward<T>(t))>);
Now you have an exact match between the deduced type for T and the expected type for A.
Unfortunately I don't know how to make the above work with variadic templates...
I have the following class:
class FunctionCallback
{
public:
static CallbackHandle Create(const std::function<void(void)> &function);
template<typename T,typename... TARGS>
static CallbackHandle Create(const std::function<T(TARGS...)> &function);
};
I then call 'Create' like this:
FunctionCallback::Create<void,float>([](float f) -> void {}); // Whether I use a lambda-function or a function pointer makes no difference
Even though that should be correct (?), visual studio underlines that line in read with the message:
Error: no instance of overloaded function "FunctionCallback::Create" matches the argument list
argument types are: (lambda []void (float f)->void)
However the program compiles fine in visual studio without any warnings or errors.
g++-5 is unable to compile it altogether with a similar message.
When changing it to:
FunctionCallback::Create<void,float>(std::function<void(float)>([](float f) -> void {}));
It doesn't display the message anymore and compiles both on windows and linux.
Why can't it deduce the type properly unless I explicitly specify it?
You almost never want to deduce the type of a type-eraser, that std::function is an example of. With a few exceptions, template type deduction attempts to deduce the exact type of an argument. For a std::function<T>-type parameter this means that the compiler can deduce T if the corresponding argument is a std::function as well. The type of a lambda expression is not a std::function.
Even if you explicitly specify type template arguments, the deduction still continues after corresponding types have been substituted with the arguments. In the case of a parameter pack, the compiler still tries to deduce the rest of arguments, so for FunctionCallback::Create<void,float> the compiler ends up with:
template <typename... TARGS>
static CallbackHandle Create(std::function<void(float, TARGS...)> function);
and this won't match anything else than std::function<void(float, TARGS...)> (or something that derives from this type); otherwise the deduction for TARGS fails.
If your goal is to always specify the parameter pack's types explicitly, you can put that pack (or the entire parameter) in a non-deduced context:
template <typename T> struct identity { using type = T; };
template <typename T> using identity_t = typename identity<T>::type;
struct FunctionCallback
{
template <typename T, typename... TARGS>
static void Create(identity_t<std::function<T(TARGS...)>> function);
};
FunctionCallback::Create<void, float>([](float f) -> void {});
DEMO
In this case, however, you'll have to pay the price of type-erasure. Instead, you can consider accepting any function object type:, letting the compiler to deduce the exact type of the argument:
struct FunctionCallback
{
template <typename F>
static void Create(F function){}
};
FunctionCallback::Create([](float f) -> void {});
DEMO 2