简体   繁体   中英

C++ equivalent syntax for a C# generic delegate, and usage with lambdas

In C# I can do this:

delegate void myFunctionDelegate<T>(T arg);

In C++, I understand that I need to use an alias to a template for a function pointer, but the syntax is so bizaare that all of the examples I find just confuse me more.

The following is wrong; how can I correct it?

template<typename T>
using myFunctionDelegate = void (*)(T arg);

I want to use it like so:

template<class T> class Foo
{
    ...
    void someOtherFunction(myFunctionDelegate callback)
    {
        ...
        callback(someValue);
    }
}

and then:

myClassInstance.someOtherFunction([&](T arg) {
    // do something with the callback argument
});

What you have almost works syntactically; the use of myFunctionDelegate simply needs a type argument:

void someOtherFunction(myFunctionDelegate<T> callback)
                                         ^^^

And the alias parameter names are optional if you aren't getting any particular benefit from them:

template<typename T>
using myFunctionDelegate = void(*)(T);

However, there is a larger problem: function pointers don't handle state. The lambda used in your sample call uses state by the capturing it does. Thus, a capturing lambda cannot be converted to a function pointer. When it's so handy to pass in such a lambda, function arguments should support that.

There are two common ways of doing so. The first is to forget about forcing a specific return and parameter type. Instead, let the caller pass any object (lambda, function pointer, functor, the result of std::bind ) that can be called the way your function calls it:

template<typename Callable>
void someOtherFunction(Callable callback) {
    ...
    callback(someValue);
}

If the call doesn't work, the code will fail to compile 1 (with an error that unfortunately isn't too helpful, but the future Concepts additions can easily help there).

On the other hand, you might want to explicitly specify the function type. C++ has a general-purpose type to store any callable object (see the above list). That type is std::function . It's a bit more heavyweight than a simple template parameter, but useful when you need it.

template<typename T>
using myFunctionDelegate = std::function<void(T)>;

void someOtherFunction(const myFunctionDelegate<T> &callback) {...}

[1]: This isn't always true (see SFINAE), but it probably will be as far as you're concerned.

std::function<void(T)> myFunctionDelegate is the (very) rough equivalent of delegate void myFunctionDelegate<T>(T arg)

std::function<void(T)> follows value semantics (it behaves more like an int than a C# object reference) which makes things different.

A lambda closure ( [](T t){/*code*/} ) whose lifetime (or copies of it) outlives the local scope should not use & based capture. Instead use = based capture (which may require extra work). If the code you are calling does not store a copy of the delegate beyond the lifetime of the call, [&] is optimal. In C++ the lifetime of data is something you need concern yourself with.

This is not intended as a full tutorial on how lambdas and std::function work, but just to point you in the right direction.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM