lambda converted to bool instead of deducing function-pointer-type - c++

I wanted to implement a overload for operator<< that allowed me to call a given function and output the result.
I therefore wrote an overload, but the conversion to bool is selected and when writing a function myself, it would not compile.
EDIT: Know that I do not want to call the lambda,
but instead pass it to the function where it should be called with a default constructed parameter list.
I have appended my code:
#include <iostream>
template<typename T>
void test(T *) {
std::cout << "ptr" << std::endl;
}
template<typename T>
void test(bool) {
std::cout << "bool" << std::endl;
}
template<typename Ret, typename ...Args>
void test(Ret(*el)(Args...)) {
std::cout << "function ptr\n" << el(Args()...) << std::endl;
}
template<typename Char_T, typename Char_Traits, typename Ret, typename ...Args>
std::basic_ostream<Char_T, Char_Traits>& operator<<(
std::basic_ostream<Char_T, Char_Traits> &str, Ret(*el)(Args...)) {
return str << el(Args()...);
}
int main() {
std::boolalpha(std::cout);
std::cout << []{return 5;} << std::endl; // true is outputted
test([]{return 5;}); // will not compile
}
I use gcc 7.3.1 with the version flag -std=c++14.
EDIT: Error message:
main.cc: In function ‘int main()’:
main.cc:25:23: error: no matching function for call to ‘test(main()::<lambda()>)’
test([]{return 5;});
^
main.cc:5:6: note: candidate: template<class T> void test(T*)
void test(T *) {
^~~~
main.cc:5:6: note: template argument deduction/substitution failed:
main.cc:25:23: note: mismatched types ‘T*’ and ‘main()::<lambda()>’
test([]{return 5;});
^
main.cc:9:6: note: candidate: template<class T> void test(bool)
void test(bool) {
^~~~
main.cc:9:6: note: template argument deduction/substitution failed:
main.cc:25:23: note: couldn't deduce template parameter ‘T’
test([]{return 5;});
^
main.cc:13:6: note: candidate: template<class Ret, class ... Args> void test(Ret (*)(Args ...))
void test(Ret(*el)(Args...)) {
^~~~
main.cc:13:6: note: template argument deduction/substitution failed:
main.cc:25:23: note: mismatched types ‘Ret (*)(Args ...)’ and ‘main()::<lambda()>’
test([]{return 5;});

Your problem here is that Template Argument Deduction is only done on the actual argument passed to test. It's not done on all possible types that the argument could possibly converted to. That might be an infinite set, so that's clearly a no-go.
So, Template Argument Deduction is done on the actual lambda object, which has an unspeakable class type. So the deduction for test(T*) fails as the lambda object is not a pointer. T can't be deduced from test(bool), obviously. Finally, the deduction fails for test(Ret(*el)(Args...)) as the lambda object is not a pointer-to-function either.
There are a few options. You might not even need a template, you could accept a std::function<void(void)> and rely on the fact that it has a templated constructor. Or you could just take a test(T t) argument and call it as t(). T will now deduce to the actual lambda type. The most fancy solution is probably using std::invoke, and accepting a template vararg list.

Even though non-capturing lambdas have an implicit conversion to function pointers, function templates must match exactly for deduction to succeed, no conversions will be performed.
Therefore the easiest fix is to force the conversion with a +
int main() {
std::boolalpha(std::cout);
std::cout << []{return 5;} << std::endl; // true is outputted
test(+[]{return 5;});
// ^
}

template<typename T>
void test(bool) {
std::cout << "bool" << std::endl;
}
Template is not needed. In fact you overload functions, not templates. Replace it with
void test(bool) {
std::cout << "bool" << std::endl;
}
Now your sample will compile.

Related

std::invoke - perfect forwarding functor

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.

Why can't this parameter pack accept function pointers?

I'm trying to create a parameter pack full of function pointers, but GCC (with c++17 standard) generates a deduction failed error. Why is that?
As written here:
For pointers to functions, the valid arguments are pointers to functions with linkage (or constant expressions that evaluate to null pointer values).
In my example, that's the case (isn't it?).
Is this rule invalidated for parameter packs? Did I miss something in the standard? If that's the case, how can I fix my code, without passing the function pointers as function arguments (ie without declaring T run2(T input, Funcs... funcs).
// In f.hpp
template<typename T>
T run2(T input)
{
return input;
}
template<typename T, T(*f)(T), class ... Funcs>
T run2(T input)
{
return run2<T, Funcs...>(f(input));
}
// In m.cpp
unsigned add2(unsigned v)
{
return v+2;
}
int main()
{
unsigned a=1;
a = run2<unsigned, add2>(a); // works
a = run2<unsigned, add2, add2>(a); // doesn't work
std::cout << a << std::endl;
return 0;
}
This the error I get with run2<unsigned, add2, add2> (GCC doesn't tell me why the last attempt actually failed):
m.cpp: In function ‘int main()’:
m.cpp:37:37: error: no matching function for call to ‘run2(unsigned int&)’
a = run2<unsigned, add2, add2>(a);
^
In file included from m.cpp:2:0:
./f.hpp:85:3: note: candidate: template<class T> T run2(T)
T run2(T input)
^
./f.hpp:85:3: note: template argument deduction/substitution failed:
m.cpp:37:37: error: wrong number of template arguments (3, should be 1)
a = run2<unsigned, add2, add2>(a);
^
In file included from m.cpp:2:0:
./f.hpp:109:3: note: candidate: template<class T, T (* f)(T), class ... Funcs> T run2(T)
T run2(T input)
^
./f.hpp:109:3: note: template argument deduction/substitution failed:
You declared a type parameter pack, class... Funcs. You can't pass function pointers as arguments for type parameters, because they are values, not types. Instead, you need to declare the run2 template so that it has a function pointer template parameter pack. The syntax to do so is as follows:
template<typename T, T(*f)(T), T(*...fs)(T)>
T run2(T input)
{
return run2<T, fs...>(f(input));
}
(The rule is that the ... is part of the declarator-id and goes right before the identifier, namely fs.)
The pack fs can accept one or more function pointers of type T (*)(T).

auto-returning functions and template instantiation

While writing some template code, I ran into <unresolved overloaded function type> errors which can be reduced to the following.
template <int N>
auto bar()
{
return N;
}
int main(int, char* [])
{
auto foo = [] (auto func) {
return func();
};
foo(bar<3>);
}
With the errors being:
unresolved_overload.cpp: In function 'int main(int, char**)':
unresolved_overload.cpp:26:28: error: no match for call to '(main(int, char**)::<lambda(auto:1)>) (<unresolved overloaded function type>)'
std::cout << foo(bar<3>) << std::endl;
^
unresolved_overload.cpp:21:29: note: candidate: template<class auto:1> constexpr main(int, char**)::<lambda(auto:1)>::operator decltype (((const main(int, char**)::<lambda(auto:1)>*)((const main(int, char**)::<lambda(auto:1)>* const)0))->operator()(static_cast<auto:1&&>(<anonymous>))) (*)(auto:1)() const
auto foo = [] (auto func) {
^
unresolved_overload.cpp:21:29: note: template argument deduction/substitution failed:
unresolved_overload.cpp:26:28: note: couldn't deduce template parameter 'auto:1'
std::cout << foo(bar<3>) << std::endl;
^
unresolved_overload.cpp:21:29: note: candidate: template<class auto:1> main(int, char**)::<lambda(auto:1)>
auto foo = [] (auto func) {
^
unresolved_overload.cpp:21:29: note: template argument deduction/substitution failed:
unresolved_overload.cpp:26:28: note: couldn't deduce template parameter 'auto:1'
std::cout << foo(bar<3>) << std::endl;
If we replace the auto-return with the explicit return type, int, the example will compile fine.
Why does auto-return run into these issues? I looked into template argument deduction and substitution but the search was largely unfruitful. I thought it might have something to do with the order of template instantiation / etc but couldn't make too much sense of it...
Per AndyG's suggestion, I found the same issue on GCC's bug list. Bug 64194. First reported in 2014. Thus the conclusion seems to be that this is a GCC bug and thankfully not another special case for templates.
Working around this just requires having something else to trigger the instantiation (e.g. assign to a variable, a using declaration).
Try this:
template <typename func>
auto bar(func&& f)->decltype(f())
{
return f();
}
int main()
{
int i = 100;
auto f = [=]()
{
return i;
};
bar(f);
return 0;
}

C++: Template argument deduction when passing template functions as argument to other template functions

I have the following snippet of example code:
The idea is that there is a container class, here called Box, and that we might want to create new versions of this container by mapping a function over its current contents.
#include <iostream>
#include <tuple>
template <typename TContent>
class Box
{
TContent d_content;
public:
Box(TContent content)
:
d_content(content)
{}
TContent content() const
{
return d_content;
}
template <typename Function>
auto transform(Function fun) -> decltype(Box{fun(d_content)})
{
return Box{fun(d_content)};
};
};
template <typename TElem>
std::tuple<TElem, TElem> toTuple(TElem thing)
{
std::cout << "Transforming " << thing << "to tuple.\n";
return std::make_tuple(thing, thing);
}
int main() {
std::cout << "Hello World!\n";
Box<int> mybox{10};
Box<int> box2 = mybox.transform([](int content){ return content * 2;});
std::cout << "Transformed box: " << box2.content() << '\n';
Box<std::tuple<int, int>> box3 = mybox.transform(&toTuple); // <- Template argument deduction/substitution fails here!
std::cout << "Transformed box: " << std::get<0>(box3.content()) << '\n';
}
Try it out here
At line 42, where box3 is created, template argument deduction/substitution fails:
main.cpp: In function 'int main()':
main.cpp:42:60: error: no matching function for call to 'Box<int>::transform(<unresolved overloaded function type>)'
Box<std::tuple<int, int>> box3 = mybox.transform(&toTuple);
^
main.cpp:22:8: note: candidate: template<class Function> decltype (Box<TContent>{fun(((Box<TContent>*)this)->Box<TContent>::d_content)}) Box<TContent>::transform(Function) [with Function = Function; TContent = int]
auto transform(Function fun) -> decltype(Box{fun(d_content)})
^~~~~~~~~
main.cpp:22:8: note: template argument deduction/substitution failed:
main.cpp:42:60: note: couldn't deduce template parameter 'Function'
Box<std::tuple<int, int>> box3 = mybox.transform(&toTuple);
^
exit status 1
This seems to be the case when attempting to pass a template function (function template?) to a function that itself expects a template argument parameter.
The only way to avoid this that I've found thus far is to wrap all template functions in lambdas or other, non-template functions. This is of course suboptimal, as it introduces a lot of boilerplate.
Why is template argument deduction failing in this case, and is there a way to alter the code of the Box class (and/or its transform member function) to ensure that template argument deduction does work?
(The given code is C++11 as repl.it does not yet support c++14. The main difference in C++14 would be that the trailing return type of transform can be omitted. The error, though, remains the same. I am happy with solutions that (only) work in C++14 as well.)
In your example here:
template <typename Function>
auto transform(Function fun) -> decltype(Box{fun(d_content)})
Box is a class template, not a class. Inside the class, Box is the injected-class-name, it always refers specifically to Box<TContent>. So if you need to change the type (as transform may very well need to do), this won't work. You need to specify which Box you want based on what Function is:
template <class Function,
class U = std::result_of_t<Function&(TContent)>>
Box<U> transform(Function fun)
{
return Box<U>{fun(d_content)}; // or even better, Box<U>{std::invoke(fun, d_content)}
};
The second problem is when you call it:
Box<std::tuple<int, int>> box3 = mybox.transform(&toTuple);
toTuple is a function template, not a function. You can't pass it to another function template as it doesn't have a type, it can't be deduced. As before, you need to specify which toTuple you want:
Box<std::tuple<int, int>> box3 = mybox.transform(toTuple<int>);
Or wrap the whole thing in a lambda (this is a simplified implementation which doesn't care about copies, references, or SFINAE):
Box<std::tuple<int, int>> box3 = mybox.transform([](auto x) { return toTuple(x); });

Unresolved overloaded function type when attempting to pass a function as an argument

I receive the following error when attempting to compile a lone C++ testing file under G++ with C++11 in place.
spike/cur_spike.cpp: In function ‘int main()’:
spike/cur_spike.cpp:60:44: error: no matching function for call to ‘callFunctionFromName(<unresolved overloaded function type>, std::__cxx11::string&)’
callFunctionFromName (outputLine, param);
^
spike/cur_spike.cpp:49:7: note: candidate: template<class funcT, class ... Args> funcT callFunctionFromName(funcT (*)(Args ...), Args ...)
funcT callFunctionFromName (funcT func(Args...), Args... args) {
^
spike/cur_spike.cpp:49:7: note: template argument deduction/substitution failed:
spike/cur_spike.cpp:60:44: note: couldn't deduce template parameter ‘funcT’
callFunctionFromName (outputLine, param);
^
Here's the code.
#include <iostream>
#include <string>
template <typename T>
void outputLine (T text) {
std::cout << text << std::endl;
}
template <typename funcT>
funcT callFunctionFromName (funcT func()) {
return func ();
}
template <typename funcT, typename... Args>
funcT callFunctionFromName (funcT func(Args...), Args... args) {
return func (args...);
}
int main () {
std::string param = "Testing...";
callFunctionFromName (outputLine, param);
return 0;
}
I'm currently building this on a Linux system using G++, this code snippet contains all code related to this issue. I'm particularly intrigued about the fact that the compiler is unable to deduce the template parameter funcT for some reason, even though the outputLine function has a clear return type of void.
Setting a pointer of outputLine using void (*outputLinePtr)(std::string) = outputLine and using the pointer as the argument instead does nothing. Any help would be much appreciated.
You can manually set template argument.
callFunctionFromName (outputLine<std::string>, param);