0

Given the two function templates below:

template <typename T>
void gorp(T*, std::function<void(T*)>)
{
}

template <typename T>
void klop(T*, std::function<void()>)
{
}

The function klop() works as I expect, but gorp() does not:

int x = 3;

//fails: candidate template ignored: could not match 
//'function<void (type-parameter-0-0 *)>' against '(lambda at ...)
gorp( &x, [](int*){}); //1

//works
gorp<int>( &x, [](int*){}); //2

//fails: candidate template ignored: could not match 
//'function<void (type-parameter-0-0 *)>' against 'void (^)(int *)'
gorp( &x, ^(int*){}); //3

//works
gorp<int>( &x, ^(int*){}); //4

//works
klop( &x, [](){}); //5

//works
klop( &x, ^(){}); //6

Lines 3,4,6 use clang blocks; lines 1,2,5 use lambdas.

Notice the calls to klop() have no problem inferring T, but I have to help out the calls to gorp().

I'm at a loss. Shouldn't it be doubly easy for gorp() to figure out T?

273K
  • 29,503
  • 10
  • 41
  • 64
Mustang
  • 363
  • 2
  • 9
  • 3
    `^(int*){}` - this is not C++. What language dialect do you use? – 273K Apr 02 '23 at 06:33
  • As I mention, `^(int*){}` are clang "blocks"--a many-years-old now extension to C/C++ in Apple's compiler. (They may even predate C++ lambdas.) They interoperate with std::function. Just ignore those lines if you're not familiar with Apple's tools. – Mustang Apr 02 '23 at 06:44
  • 2
    If you want to use non-standard and non-portable extensions then that's your business (even though I really recommend against them), but please try to avoid it in your questions here. Unless the question is *specifically* about that extension, but then you need to explicitly state so and tag the question accordingly. – Some programmer dude Apr 02 '23 at 06:47
  • @RemyLebeau why would `T` be deduced as `int*` instead of `int`? `klop` doesn't work that way? – Mustang Apr 02 '23 at 06:47
  • @273K see [Language Specification for Blocks](https://clang.llvm.org/docs/BlockLanguageSpec.html) in clang's documentation. – Remy Lebeau Apr 02 '23 at 06:47
  • @Mustang that's my point, `T` *wouldn't* be deduced as `int*`, as Someprogrammerdude had claimed. But I see his comment has been deleted. – Remy Lebeau Apr 02 '23 at 06:48
  • @Someprogrammerdude sorry about the block distraction. They work and fail in exactly the same circumstances. Just ignore those lines please. – Mustang Apr 02 '23 at 06:49
  • 1
    @Mustang "*Just ignore those lines*" - then you should [edit] the question to remove them altogether, since they are not relevant to the question and are just a distraction. – Remy Lebeau Apr 02 '23 at 06:51
  • @RemyLebeau I explicitly wanted to demonstrate to those familiar that they succeed and fail in exactly the same way as lambdas. I'm simply inviting those not familiar that they are free to ignore them. – Mustang Apr 02 '23 at 06:53

2 Answers2

2

If you must have std::function, you can inhibit type deduction for this argument:

template<typename T>
void gorp (T* t, std::function<void(std::type_identity_t<T*>)> func);

int x;
gorp(&x, [](int*){}); // works

However I would recommend

template <typename T, typename F>
void gorp(T* t, F&& f) requires(requires {f(t);})
{
    f(t);
}

This accepts everything the previous version accepts, and more, and does so more efficiently without conversion to std::function.

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
  • Thanks for the extra advice. I should have mentioned in the post that I can't use C++20. So for now, I'm just dropping the `requires` clause. – Mustang Apr 02 '23 at 18:34
1

It won't infer the type T because you're not passing a std::function. The type inference doesn't work if a conversion is also required. This does work:

gorp(&x, std::function<void(int*)>([](int*) {}));

And so does this, using plain function pointers:

template <typename T>
void gorp(T*, void(*)(T*))
{
}

gorp(&x, +[](int*) {}); // + converts captureless lambda to function pointer

Or to support Clang "blocks":

template <typename T>
void gorp(T*, void(^)(T*))
{
}

And finally, to support all of the above with a single definition:

template <typename T, typename F>
void gorp(T*, F&& f)
{
}

This can be more efficient than a definition taking std::function if the function is called many times, as std::function does carry some overhead.

John Zwinck
  • 239,568
  • 38
  • 324
  • 436
  • Can you explain how the `+` trick works. I haven't seen that before. I always thought that `std::function` was the way to please all clients--but `F&&` is _much_ better – Mustang Apr 02 '23 at 18:32
  • The `+` forces the lambda to be evaluated in an arithmetic context, which implicitly converts it to a pointer (to a function). – John Zwinck Apr 04 '23 at 17:59