luk*_*k32 13 c++ lambda generic-lambda c++14
我有一个玩具示例,我想在架构上进行修改以删除Processoron的类型依赖性EmitterT:
#include <iostream>
#include <utility>
using namespace std;
struct Emitter {
void e(int) { cout << "emitting int\n";}
void e(double) { cout << "emitting double\n";}
void e(char*) { cout << "emitting char*\n";}
void e(const char*) { cout << "emitting const char*\n";}
};
template <typename EmitterT>
struct Processor {
Processor(EmitterT e) : e_{e} {}
template <typename T>
void process(T&& value) {
cout << "some processing... ";
e_(std::forward<T>(value));
}
EmitterT e_;
};
template<typename Emitter_>
Processor<Emitter_> makeProcessor(Emitter_ e) { return Processor<Emitter_>(e);}
int main() {
Emitter em;
auto p = makeProcessor([&em](auto v){em.e(v);});
p.process(1);
p.process("lol");
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我想将负责利用处理结果的部分与处理本身分离。本Emitter类结构,是给我,所以我要支持重载函数。
我想将lambda函数传递给将使用该函数的处理器。有点像回调机制,但是它必须是通用lambda,以支持重载。
我写的示例有效,但它取决于Emitter类型作为模板参数。我不喜欢Processor根据更改类型Emitter。它也具有传染性,我具有真正的Processor等级结构,并且Emitter传播程度const甚至更差。
在阅读/sf/answers/1206355461/之后,我尝试使用以下struct作为成员:
struct EmitterC {
template<typename T>
void operator()(T value) { }
};
Run Code Online (Sandbox Code Playgroud)
但是我无法找到一种将其用作常规参数Emitter后推迟执行的方法Processor。它具有前向声明和引用,EmitterC&但仅支持一个Emitter定义。我想出的唯一方法是删除lambda,并对EmitterC我期望的每种类型进行虚拟重载,Emitter并将其用作基类。
因此,有没有办法将(通用)lambda作为参数传递,从而使Processor类型不依赖于Emitter?
我仅限于c ++ 14,但如果有更好的支持,我也对更现代的标准感兴趣。
最简单的解决方案是为Emitter以下参数设置参数process:
struct Processor {
template <typename T, typename EmitterFn>
void process(T&& value, EmitterFn emit) {
cout << "some processing... ";
emit(std::forward<T>(value));
}
};
Run Code Online (Sandbox Code Playgroud)
但是,如果它必须是其中的一员Processor并且可以枚举可能的函数签名,则可以使用某种类型的擦除。std::function否则提出的建议std::function_ref将无法使用,因为它们只允许一个函数签名,但是我们可以编写自己的函数overloaded_function_ref:
template <typename Derived, typename Sig>
class function_ref_impl;
template <typename Derived, typename R, typename... Args>
class function_ref_impl<Derived, R(Args...)> {
using fn_t = R(*)(void const*, Args...);
public:
auto operator()(Args... args) const -> R {
return fn(static_cast<Derived const&>(*this).object, std::forward<Args>(args)...);
}
protected:
template <typename F,
std::enable_if_t<!std::is_base_of<function_ref_impl, F>::value, int> = 0>
explicit function_ref_impl(F const& f)
: fn{[](void const* self, Args... args) -> R {
return (*static_cast<F const*>(self))(std::forward<Args>(args)...);
}}
{}
private:
fn_t fn;
};
template <typename... Sig>
class overloaded_function_ref
: public function_ref_impl<overloaded_function_ref<Sig...>, Sig>...
{
public:
template <typename F,
std::enable_if_t<!std::is_base_of<overloaded_function_ref, F>::value, int> = 0>
overloaded_function_ref(F const& f)
: function_ref_impl<overloaded_function_ref, Sig>(f)...
, object{std::addressof(f)}
{}
// Can be done pre-C++17, but it's not easy:
using function_ref_impl<overloaded_function_ref, Sig>::operator()...;
// This can be encapsulated with techniques such as the "passkey" idiom.
// Variadic friend expansion isn't a thing (`friend bases...`).
void const* object;
};
Run Code Online (Sandbox Code Playgroud)
这确实需要C ++ 17的using
/* base */::operator()...,但可以在C ++ 14被仿真; 请参阅介绍此功能的论文:[P0195],或者可以按摩Boost HOFmatch来执行此操作。这也是一个函数引用,而不是一个拥有函数。
然后我们可以写:
struct Processor {
template <typename T>
void process(T&& value) {
cout << "some processing... ";
emit(std::forward<T>(value));
}
using emitter_t = overloaded_function_ref<
void(int),
void(double),
void(char*),
void(char const*)
>;
emitter_t emit;
};
Run Code Online (Sandbox Code Playgroud)
恕我直言:继承是在这里。
#include <iostream>
#include <utility>
using namespace std;
struct BaseEmitter {
virtual void e(int) =0;
virtual void e(double)=0;
virtual void e(char*)=0;
virtual void e(const char*)=0;
};
struct Emitter :public BaseEmitter {
virtual void e(int) { cout << "emitting int\n";}
virtual void e(double) { cout << "emitting double\n";}
virtual void e(char*) { cout << "emitting char*\n";}
virtual void e(const char*) { cout << "emitting const char*\n";}
};
struct Processor {
BaseEmitter& e_;
Processor(BaseEmitter& e) : e_(e) {}
template <typename T>
void process(T&& value) {
cout << "some processing... ";
e_(std::forward<T>(value));
}
};
int main() {
Emitter em;
auto p = Processor(em);
p.process(1);
p.process("lol");
return 0;
}
Run Code Online (Sandbox Code Playgroud)
您可以通过在接口中进行继承来混合以捕获lambda:
struct bypass
{
virtual void operator()() = 0;
};
template<typename callable> struct capture: public bypass
{
callable& _ref;
capture(callable &ref)
: _ref(ref)
{;};
virtual void operator()()
{
_ref();
}
};
struct test
{
bypass *_c;
template<class T> test(T& callback)
: _c(nullptr)
{
_c = new capture<decltype(callback)>(callback);
};
void doit()
{
(*_c)();
}
};
int main(int argc, char* argv[])
{
auto lambda = [](){std::cout << "hello\n";};
test z=test(lambda);
z.doit();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
是否可以将通用 lambda 作为非模板参数传递
无法声明接受 lambda 作为参数的非模板函数。lambda 的类型是匿名的:它没有名称。不可能编写接受匿名类型参数的函数声明。
lambda 的类型是可以推导的,这就是为什么 lambda 可以传递到其参数类型被推导的函数模板中。
虽然这回答了问题,但它并没有提供解决方案。我认为解决方案不会那么简单。