gpt4 book ai didi

c++ - 是否可以将通用 lambda 作为非模板参数传递

转载 作者:IT老高 更新时间:2023-10-28 21:43:39 26 4
gpt4 key购买 nike

我有一个玩具示例,我想在架构上进行修改以消除 ProcessorEmitterT 的类型依赖性:

#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;
}

动机

我想将负责利用处理结果的部分与处理本身分离。 Emitter 类结构给了我,所以我必须支持重载函数。

我想将 lambda 函数传递给将要使用它的处理器。有点像回调机制,但它必须是通用 lambda,才能支持重载。

我尝试过的:

我写的例子有效,但它依赖于 Emitter 类型作为模板参数。我不喜欢根据 Emitter 更改 Processor 类型。它也具有传染性,我有一个真正的 Processor 层次结构和 Emitterconst 或更糟的传播方式。

看完https://stackoverflow.com/a/17233649/1133179我尝试以成员身份使用以下结构:

struct EmitterC {
template<typename T>
void operator()(T value) { }
};

但是当将 Emitter 用作普通参数时,我无法想出一种方法来推迟 Processor 之后的 Emitter 实现。它使用前向声明和引用 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));
}

};

但是,如果它必须是 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;
};

Live example

确实需要 C++17 才能使用
/* base */::operator()...
,但可以在 C++14 中模拟;请参阅介绍此功能的论文:[P0195] , 或者 Boost HOF's match可以通过按摩来做到这一点。这也只是一个函数引用,而不是一个拥有函数。

那么我们可以这样写:

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;
};

Demo

关于c++ - 是否可以将通用 lambda 作为非模板参数传递,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57149664/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com