我正在编写一个库,用户将回调作为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";});
}
或者,如果有更好的方法可以做到这一点。
答案 0 :(得分:5)
表达SFINAE:
public byte[] consume(InputStream in) throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buffer = new byte[2048];
int read = -1;
while ((read = in.read(buffer)) > 0) {
out.write(buffer, 0, read);
}
return out.toByteArray();
}
然后只是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
技巧。
答案 1 :(得分:1)
我的解决方案是使用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)>
并在编译时重新连接正确的函数。