1

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;});
Deduplicator
  • 44,692
  • 7
  • 66
  • 118
Tarirah
  • 93
  • 6

3 Answers3

2

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.

MSalters
  • 173,980
  • 10
  • 155
  • 350
  • But why does it work when using cout? which overload is used here? To work around this problem, I also gave a `bool` overload to `test()`, but it will not be used – Tarirah Mar 14 '18 at 18:21
  • There is no `operator <<` for `std::ostream` and function pointer - at least the debugger directly calls `std::ostream::operator<<(bool)`. But I have no idea why a function pointer implicitly converts to `bool`. In fact any other function pointer also prints true or false (when NULL). – Marcel Mar 14 '18 at 18:48
0
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.

273K
  • 29,503
  • 10
  • 41
  • 64
0

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;}); 
    //   ^
}
Passer By
  • 19,325
  • 6
  • 49
  • 96