4

I have a very strange problem. To keep things simple, lets say I want to have a function which takes 2 functions with the same declaration as arguments

template<typename Func>
void foo(Func a, Func b)
{
    std::cout << "good";
}

To try things out I took putchar from cstdio, and created an identical function to match the putchar.

int myPutcharFunc(int)
{
    return 0;
}

int main()
{
    auto myPutcharLambda = [](int) -> int
    {
        return 0;
    };

    foo(putchar, myPutcharFunc); // okay
    foo(putchar, myPutcharLambda);  //deduced conflicting types for parameter 'Func' ('int (__attribute__((__cdecl__)) *)(int)' and 'main()::<lambda(int)>')
}

Now, the lambda does not want to compile (the key is I want to use lambda capture).

So lets add template specialization, because the programmer is wiser than the machine, right? :)

template<typename Func>
void foo(Func a, Func b)
{
    std::cout << "good";
}

template<>
void foo(int(*)(int), int(*)(int))
{
    std::cout << "good";
}

No luck, the same error - why? But for some reason, when I comment out the template specialization:

//template<>
void foo(int(*)(int), int(*)(int))
{
    std::cout << "good";
}

The code compiles. I obviously do not want to overload foo for EVERY set of function's arguments - thats what templates are for. Every step was tested both with msvc++ and g++. What am I doing wrong?

3 Answers 3

2

Two possibilities.

1: Just put + in front of the lambda:

foo(putchar, +myPutcharLambda);

That works because unary + expects an integer-like value, such as a pointer. Therefore, the lambda converts to a function pointer.

Ultimately a (non-capturing) lambda doesn't have the same type as a function pointer, even though it's willing to convert to a function pointer.

How is a compiler supposed to know which conversions are allowed to make two objects of the same type?

2: There is another option, making use of the fact that the ?: is willing to do some conversions, converting one type to another in some circumstances.

template<typename Func1, typename Func2>
void foo2(Func1 a, Func2 b)
{
    using common_type = decltype(true?a:b); // or 'false', it doesn't matter
    foo<common_type>(a,b);
}
Sign up to request clarification or add additional context in comments.

1 Comment

Option 1 is particularly naughty. :)
2

Every lambda is a different type, so you'll need to have two different template parameters to get them

template<typename FuncA, typename FuncB>
void foo(FuncA a, FuncB b)

Types don't decay when deducing template types (SEE COMMENT FOR CORRECTION). So a lambda remains a lambda and doesn't decay to a function pointer. Same reason a string literal is deduced as a char[N] instead of a const char *.

With your second example using specialization, it doesn't want to use your specialization, since the lambda is not a function pointer. You can cast the Lambda to a function pointer and make it work: https://godbolt.org/g/ISgPci The trick you can do here is say +my_lambda because + is defined for pointers so it will force the non-capturing lambda to become a function pointer.

3 Comments

"Types don't decay when deducing template types" of course they do. "Same reason a string literal is deduced as a char[N] instead of a const char *", a raw string literal is deduced as const char*
@PiotrSkotnicki when does it come in as a char array then? I was just reading about that in some other uestion.
when a function parameter is of a reference type
1

A lambda has its own type which can decay to a function pointer but not in the case of a template function match, it will for the real function as you found because of the implicit conversion.

In the case of matching to a template you need to disambiguate and explicitly instantiate foo with the type you want or convert the lambda to a function pointer.

foo<decltype(putchar)>(putchar, myPutcharLambda);

or

foo(putchar, +myPutcharLambda);

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.