5
#include <functional>
#include <iostream>

template<typename T>
void test( std::function< void ( const T& ) > f )
{
    T val {};
    f( val );

    std::cout << "std::function" << std::endl;
}

template<typename T>
void test( void(*f) ( const T& ) )
{
    T val {};
    f( val );

    std::cout << "function pointer" << std::endl;
}

int main()
{
    auto capturing_var { 0 };

    // Works because implicit conversion to function pointer isn't applied when lambda is capturing
    test< int >( [ capturing_var ]( const int& x ) { } );

    // Doesn't work because implicitly convertible to function pointer and makes ambiguity
    // I want this line to work somehow, how can i make it worked with same client code ? Is it possible ?
    test< int >( []( const int& x ) { } );

    // This line is finer if it works, but in this case compiler cannot deduce T and also ambiguous if it could
    test( []( const int& x ) { } );

    // Works because of unary + operator so conversion to function ptr and i dont need to specify T
    test( +[]( const int& x ) { } );

    return 0;
}

Actually I just want this code to work without changing anything in main(), but I'm not sure whether it is possible.

I can omit overloaded function which takes function pointer, then it would work but in that case I need to specify what T is.

Note: I need T to be deduced.

1 Answer 1

1

You can use technique described in this question with some small postprocessing.

Minimal example:

template<typename Ret, typename Arg>
Arg argument_type(Ret(*)(Arg));

template<typename Ret, typename Fn, typename Arg>
Arg argument_type(Ret(Fn::*)(Arg) const);

template<typename Fn>
auto argument_type(Fn) -> decltype(argument_type(&Fn::operator()));

template<typename Arg, typename Fn>
struct argument {
    static_assert(std::is_invocable_v<Fn, Arg>);
    using type = Arg;
};

template<typename Fn>
struct argument<void, Fn>{
    using type = decltype(argument_type(std::declval<Fn>()));
};

template<typename T = void, typename Fn>
void test(Fn fn) {
    using Arg = std::decay_t<typename argument<T, Fn>::type>;
    std::cout << "Arg = " << boost::typeindex::type_id_with_cvr<Arg>().pretty_name() 
              << std::endl;
}

int main() {
    int capturing_var;

    test<int>([capturing_var](const int& x) {}); // Arg = int
    test<int>([](const int& x) {});              // Arg = int
    test([](const int& x) {});                   // Arg = int 
    test(+[](const int& x) {});                  // Arg = int
    test<int>([](auto x) {});                    // Arg = int
}

If the argument type cannot be deduced, e.g., for a variadic lambda, it has to be provided (last example).

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.