Lambda 回调取决于其参数

Lambda callback depends on its parameters

我正在编写一个库,用户可以在其中以 lambda 形式提供回调。在默认情况下,我只想调用 lambda 并传回一个对象。

现在有一些重要的场景,用户可能也需要上下文。所以我希望能够使用相同的回调机制,只允许用户将上下文作为参数添加到他们的 lambda 中,然后我将传递对象和上下文。

我不能完全让 SFINAE 工作。

我已将代码简化为:

#include <string>
#include <iostream>

class Context {};


template<typename F>
struct UseContext
{
    // I want to set this value to 0 or 1 based on the parameters
    // in F but can't quite get this to work.
    enum {value = 0 };
};


template<typename F, typename T, bool useContext = UseContext<F>::value>
struct Caller;

template<typename F, typename T>
struct Caller<F, T, true>
{
    void operator()(F& func, Context& context, T& object)
    {
        func(context, object);
    }
};
template<typename F, typename T>
struct Caller<F, T, false>
{
    void operator()(F& func, Context&, T& object)
    {
        func(object);
    }
};



template<typename T, typename F>
void doWork(F&& func)
{
  Context  context;
  T        object;

  /// STUFF
  Caller<F,T>  caller;
  caller(func, context, object);
}

用法:

int main()
{
    // if UseContext::value == 0 then this compiles.
    // This is the normal situation.
    doWork<std::string>([](std::string const& x){ std::cout << x << "\n";});

    // if UseContext::value == 1 then this compiles.
    // This is if the user wants more context about the work.
    // most of the time this extra parameter is not required.
    // So I don't want to force the user to add it to the parameter
    // list of the lambda.
    doWork<std::string>([](Context&, std::string const& x){ std::cout << x << "\n";});
}

或者如果有更好的方法。

SFINAE 表达式:

template<class F, class T>
auto call(F& func, Context& context, T& object) -> decltype(func(context, object), void())
{
   func(context, object);
}
template<class F, class T>
auto call(F& func, Context&, T& object) -> decltype(func(object), void())
{
    func(object);
}

然后call(func, context, object)。如果两种形式都有效,这是不明确的。如果您想消除歧义,只需添加一个虚拟参数并执行通常的 int/long 技巧。

我的解决办法是用std::is_constructible加上std::enable_if:

template<typename F,typename T>
typename std::enable_if<std::is_constructible<std::function<void(T const&)>,F>::value>:type doWork(F func)
{
//...
}

template<typename F,typename T>
typename std::enable_if<std::is_constructible<std::function<void(Context&,T const&)>,F>::value>:type doWork(F func)
{
//...
}

explenation - 每个 std::function 都可以从等价的 lambda 构建。我们在这里使用 std::enable_if 测试您是否可以构建 std::function<void(T)>std::function<void(Context,T)> 并在编译时重新连接正确的函数。