There is a requirement where I need to pass an rvalue from 1 function to another function via variadic template. To avoid real code complexity, minimal example is below using int:
void Third (int&& a)
{}
template<typename... Args>
void Second (Args&&... args) {
Third(args...);
}
void First (int&& a) {
Second(std::move(a)); // error: cannot bind ‘int’ lvalue to ‘int&&’
Third(std::move(a)); // OK
}
int main () {
First(0);
}
First(0) is called properly. If I invoke Third(int&&) directly then it works fine using std::move(). But calling Second(Args&&...) results in:
error: cannot bind ‘int’ lvalue to ‘int&&’
Third(args...); ^
note: initializing argument 1 of ‘void Third(int&&)’
void Third (int&& a)
What is the correct way to achieve the successful compilation for Second(Args&&...)?
FYI: In real code the Second(Args&&...) is mix of lvalues, rvalues and rvalue references. Hence if I use:
Third(std::move(args...));
it works. But when there are mix of arguments, it has problems.
You have to use std::forward:
template<typename... intrgs>
void Second (intrgs&&... args) {
Third(std::forward<intrgs>(args)...);
}
To preserve the rvalue-ness, you have to move or forward the parameter(s)
template<typename... intrgs>
void Second (intrgs&&... args) {
Third(std::forward<intrgs>(args)...);
}
Related
Trying to understand why the following example fails to compile:
#include <functional>
template <typename F>
void f1(F&& f)
{
std::forward<F>(f)("hi");
}
template <typename F>
void f2(F&& f)
{
std::invoke(f, "hi"); // works but can't perfect forward functor
}
template <typename F>
void f3(F&& f)
{
std::invoke<F>(f, "hi");
}
int main()
{
f1([](const char*) {}); // ok
f2([](const char*) {}); // ok
f3([](const char*) {}); // error
}
cppreference says the following about std::invoke:
Invoke the Callable object f with the parameters args. As by INVOKE(std::forward<F>(f), std::forward<Args>(args)...). This overload participates in overload resolution only if std::is_invocable_v<F, Args...> is true.
So why is f3 not equivalent to f1?
std::invoke is itself a function. In your case, its first parameter is a rvalue reference while f is a lvalue, so the error occurs.
INVOKE(std::forward<F>(f), std::forward<Args>(args)...) is executed after the function std::invoke is properly selected and called. Basically, your lambda function is passed as follows:
original lambda in main -> the parameter of f3 -> the parameter of std::invoke -> the parameter of INVOKE
So the std::forward in INVOKE(std::forward<F>(f), std::forward<Args>(args)...) is used in the last step, while you need to forward the lambda in the middle step (the parameter of f3 -> the parameter of std::invoke). I guess this is where your confusion comes.
Because you need to std::forward<F>(f) to std::invoke():
template <typename F>
void f3(F&& f)
{
std::invoke<F>(std::forward<F>(f), "hi"); // works
}
Consider the difference between these two calls:
void f(const int&) { std::cout << "const int&" << std::endl; }
void f(int&&) { std::cout << "int&&" << std::endl; }
int main()
{
std::cout << "first" << std::endl;
int&& a = 3;
f(a);
std::cout << "second" << std::endl;
int&& b = 4;
f(std::forward<int>(b));
}
The output is
first
const int&
second
int&&
If you remove the const int& overload, you even get a compiler error for the first call:
error: cannot bind rvalue reference of type 'int&&' to lvalue of type 'int'
The std::forward() is necessary for passing the correct type to std::invoke().
I guess you're getting this error:
note: template argument deduction/substitution failed:
note: cannot convert ‘f’ (type ‘main()::<lambda(const char*)>’) to type ‘main()::<lambda(const char*)>&&’
That's because inside the function f3, f is an L-value, but the invoke expects an R-value. For template argument deduction/substitution to work, the types have to match EXACTLY.
When you perfect forward f to invoke, this issue is resolved as you passed an R-value originally from outside f3.
I'm trying to make a function that takes a variable number of parameters of any type, but even the simple example I made is getting an error
#include <iostream>
#include <functional>
template<class... Ts>
void callFunction(const std::function<void(Ts...)>& function, Ts... parameters)
{
function(parameters...);
}
void myFunc(const std::string& output)
{
std::cout << output << std::endl;
}
int main()
{
callFunction<const std::string&>(&myFunc, "Hello world");
return 0;
}
When I run the above code in Ideone, I get this error:
prog.cpp: In function ‘int main()’:
prog.cpp:17:57: error: no matching function for call to ‘callFunction(void (*)(const string&), const char [12])’
callFunction<const std::string&>(&myFunc, "Hello world");
^
prog.cpp:5:6: note: candidate: template<class ... Ts> void callFunction(const std::function<void(Ts ...)>&, Ts ...)
void callFunction(const std::function<void(Ts...)>& function, Ts... parameters)
^~~~~~~~~~~~
prog.cpp:5:6: note: template argument deduction/substitution failed:
prog.cpp:17:57: note: mismatched types ‘const std::function<void(Ts ...)>’ and ‘void (*)(const string&) {aka void (*)(const std::__cxx11::basic_string<char>&)}’
callFunction<const std::string&>(&myFunc, "Hello world");
A simple suggestion: receive the callable as a deduced typename, not as a std::function
I mean (adding also perfect forwarding)
template <typename F, typename ... Ts>
void callFunction(F const & func, Ts && ... pars)
{ func(std::forward<Ts>(pars)...); }
and, obviously, call it without explicating nothing
callFunction(&myFunc, "Hello world");
This as the additional vantage that avoid the conversion of the callable to a std::function.
Anyway, I see two problems in your code:
1) if you receive the functional as a std::function receiving a list ot arguments types (a variadic list in this case, but isn't important for this problem) as a list of argument of the same types, you have to be sure that the types in the two list match exactly.
This isn't your case because the function receive a std::string const & and you pass as argument a the string literal "Hello world" that is a char const [12] that is a different type.
When the types are to be deduced, this cause a compilation error because the compiler can't choose between the two types.
You could solve receiving two list of types
template <typename ... Ts1, typename Ts2>
void callFunction (std::function<void(Ts1...)> const & function,
Ts2 && ... parameters)
{ function(std::forward<Ts2>(parameters)...); }
but now we have the second problem
2) You pass a pointer function (&myFunc) where callFunction() wait for a std::function.
We have a chicken-egg problem because &myFunc can be converted to a std::function but isn't a std::function.
So the compiler can't deduce the Ts... list of types from &myFunc because isn't a std::function and can't convert &myFunc to a std::function because doesn't know the Ts... type list.
I see that you have explicated the first type in the Ts... list, but isn't enough because the Ts... list is a variadic one so the compiler doesn't know that there is only a type in the Ts... list.
A simple solution to this problem is pass the function as a simple deduced F type.
Otherwise, if you have written callFunction() with two templates types lists, you can pass a std::function to the function
std::function<void(std::string const &)> f{&myFunc};
callFunction(f, "Hello world");
but I don't think is a satisfactory solution.
Why doesn't the following code compile (in C++11 mode)?
#include <vector>
template<typename From, typename To>
void qux(const std::vector<From>&, To (&)(const From&)) { }
struct T { };
void foo(const std::vector<T>& ts) {
qux(ts, [](const T&) { return 42; });
}
The error message is:
prog.cc:9:5: error: no matching function for call to 'qux'
qux(ts, [](const T&) { return 42; });
^~~
prog.cc:4:6: note: candidate template ignored: could not match 'To (const From &)' against '(lambda at prog.cc:9:13)'
void qux(const std::vector<From>&, To (&)(const From&)) { }
^
But it doesn't explain why it couldn't match the parameter.
If I make qux a non-template function, replacing From with T and To with int, it compiles.
A lambda function isn't a normal function. Each lambda has its own type that is not To (&)(const From&) in any case.
A non capturing lambda can decay to To (*)(const From&) in your case using:
qux(ts, +[](const T&) { return 42; });
As noted in the comments, the best you can do to get it out from a lambda is this:
#include <vector>
template<typename From, typename To>
void qux(const std::vector<From>&, To (&)(const From&)) { }
struct T { };
void foo(const std::vector<T>& ts) {
qux(ts, *+[](const T&) { return 42; });
}
int main() {}
Note: I assumed that deducing return type and types of the arguments is mandatory for the real problem. Otherwise you can easily deduce the whole lambda as a generic callable object and use it directly, no need to decay anything.
If you don't need to use the deduced To type, you can just deduce the type of the whole parameter:
template<typename From, typename F>
void qux(const std::vector<From>&, const F&) { }
Correct me if I am wrong, but template parameters deduction deduces only exact types without considering possible conversions.
As a result the compiler cannot deduce To and From for To (&)(const From&) because qux expects a reference to function, but you provide a lambda which has its own type.
You have left absolutely no chance to compiler to guess what is To. Thus, you need to specify it explicitly.
Also, lambda here needs to be passed by pointer.
Finally, this version compiles ok:
template<typename From, typename To>
void qux(const std::vector<From>&, To (*)(const From&)) { }
struct T { };
void foo(const std::vector<T>& ts) {
qux<T,int>(ts,[](const T&) { return 42; });
}
You're expecting both implicit type conversions (from unnamed function object type to function reference type) and template type deduction to happen. However, you can't have both, as you need to know the target type to find the suitable conversion sequence.
But it doesn't explain why it couldn't match the parameter.
Template deduction tries to match the types exactly. If the types cannot be deduced, deduction fails. Conversions are never considered.
In this expression:
qux(ts, [](const T&) { return 42; });
The type of the lambda expression is some unique, unnamed type. Whatever that type is, it is definitely not To(const From&) - so deduction fails.
If I make qux a non-template function, replacing From with T and To with int, it compiles.
That is not true. However, if the argument was a pointer to function rather than a reference to function, then it would be. This is because a lambda with no capture is implicitly convertible to the equivalent function pointer type. This conversion is allowed outside of the context of deduction.
template <class From, class To>
void func_tmpl(From(*)(To) ) { }
void func_normal(int(*)(int ) ) { }
func_tmpl([](int i){return i; }); // error
func_tmpl(+[](int i){return i; }); // ok, we force the conversion ourselves,
// the type of this expression can be deduced
func_normal([](int i){return i; }); // ok, implicit conversion
This is the same reason why this fails:
template <class T> void foo(std::function<T()> );
foo([]{ return 42; }); // error, this lambda is NOT a function<T()>
But this succeeds:
void bar(std::function<int()> );
bar([]{ return 42; }); // ok, this lambda is convertible to function<int()>
The preferred approach would be to deduce the type of the callable and pick out the result using std::result_of:
template <class From,
class F&&,
class To = std::result_of_t<F&&(From const&)>>
void qux(std::vector<From> const&, F&& );
Now you can pass your lambda, or function, or function object just fine.
I am experimenting with the perfect forwarding feature of C++11. Gnu g++ compiler reports an ambiguity issue of function-parameter binding (the error is shown after the source code below). My question is why is it so, as following the function-parameter binding process I don't see the ambiguity. My reasoning is as follows: call to tf(a) in main() binds to tf(int&) since a is an lvalue. Then function tf forwards the lvalue reference int& a to function g hence the function void g(int &a) should be uniquely invoked. Thus I do not see the reason for ambiguity. The error disappears when the overloaded function g(int a) is removed from the code. This is strange as g(int a) cannot be a candidate for binding with int &a.
Here is my code:
void g(int &&a)
{
a+=30;
}
void g(int &a)
{
a+=10;
}
void g(int a) //existence of this function originates the ambiguity issue
{
a+=20;
}
template<typename T>
void tf(T&& a)
{
g(forward<T>(a));;
}
int main()
{
int a=5;
tf(a);
cout<<a<<endl;
}
Compilation g++ -std=c++11 perfectForwarding.cpp reports the following errors:
perfectForwarding.cpp: In instantiation of ‘void tf(T&&) [with T = int&]’:
perfectForwarding.cpp:35:7: required from here
perfectForwarding.cpp:24:3: error: call of overloaded ‘g(int&)’ is ambiguous
perfectForwarding.cpp:24:3: note: candidates are:
perfectForwarding.cpp:6:6: note: void g(int&&) <near match>
perfectForwarding.cpp:6:6: note: no known conversion for argument 1 from ‘int’ to ‘int&&’
perfectForwarding.cpp:11:6: note: void g(int&)
perfectForwarding.cpp:16:6: note: void g(int)
This is strange as g(int a) cannot be a candidate for binding with int &a.
That's not true. If you remove the g(int&) overload then g(int) will get called. When both are declared it is ambiguous, because both are viable candidates and require no conversions.
Adding on top of Jonathan Wakely's answer.
First of all, the issue has nothing to do with perfect forwarding and we can remove tf from the picture.
For the time being consider just this code:
void g(int) {}
int main() {
int a = 5; // a is an lvalue
g(a); // ok
g(std::move(a)); // std::move(a) casts a to an rvalue and this call is also ok
}
This illustrates that a function that takes a parameter by value can take both lvalues and rvalues.
Now suppose we add
void g(int &) {}
then the first call, g(a);, becomes ambigous because g(int &) can take non-const lvalues and nothing else. The second call, g(std::move(a)) is still ok and still calls g(int) because g(int &) can't take rvalues.
Now replace g(int &) with g(int &&). The latter function can take non-const rvalues only. Hence the call g(a) is ok and calls g(int). However, g(std::move(a)) is now ambiguous.
At this point it becomes obvious that if we have the three overloads together, then the two calls become ambiguous. Actually, there's no reason for having the three overloads. Depending on the type T, most often we have either
g(T) or
g(T&) or
g(const T&) or
g(const T&) and g(T&&).
I want to invoke a method from another, through a third-party function; but both use variadic templates. For example:
void third_party(int n, std::function<void(int)> f)
{
f(n);
}
struct foo
{
template <typename... Args>
void invoke(int n, Args&&... args)
{
auto bound = std::bind(&foo::invoke_impl<Args...>, this,
std::placeholders::_1, std::forward<Args>(args)...);
third_party(n, bound);
}
template <typename... Args>
void invoke_impl(int, Args&&...)
{
}
};
foo f;
f.invoke(1, 2);
Problem is, I get a compilation error:
/usr/include/c++/4.7/functional:1206:35: error: cannot bind ‘int’ lvalue to ‘int&&’
I tried using a lambda, but maybe GCC 4.8 does not handle the syntax yet; here is what I tried:
auto bound = [this, &args...] (int k) { invoke_impl(k, std::foward<Args>(args)...); };
I get the following error:
error: expected ‘,’ before ‘...’ token
error: expected identifier before ‘...’ token
error: parameter packs not expanded with ‘...’:
note: ‘args’
From what I understand, the compiler wants to instantiate invoke_impl with type int&&, while I thought that using && in this case would preserve the actual argument type.
What am I doing wrong? Thanks,
Binding to &foo::invoke_impl<Args...> will create a bound function that takes an Args&& parameter, meaning an rvalue. The problem is that the parameter passed will be an lvalue because the argument is stored as a member function of some internal class.
To fix, utilize reference collapsing rules by changing &foo::invoke_impl<Args...> to &foo::invoke_impl<Args&...> so the member function will take an lvalue.
auto bound = std::bind(&foo::invoke_impl<Args&...>, this,
std::placeholders::_1, std::forward<Args>(args)...);
Here is a demo.