gpt4 book ai didi

lambda - 将 lambda 参数完美转发到成员函数,其中成员函数是非类型模板参数

转载 作者:行者123 更新时间:2023-12-01 13:19:44 25 4
gpt4 key购买 nike

上下文

我想将一个成员函数和一个特定的对象包装到一个函数对象中(稍后我将把它用作回调)。我想为不同的成员函数和对象编写一次这个包装函数,特别是因为我的实际 lambda 在调用包装方法之前做了一些额外的工作。以下是一些可能的实现:

#include <iostream>
#include <string>
#include <utility>

template <class ClassT, class... ArgsT>
auto getCallbackPtr(ClassT* obj, void(ClassT::* memfn)(ArgsT...))
{
return [obj, memfn](ArgsT&&... args) {
(obj->*memfn)(std::forward<ArgsT>(args)...);
};
}
template <auto memFn, class ClassT>
auto getCallbackTemplate(ClassT* obj)
{
return [obj](auto&&... args){
return (obj->*memFn)(std::forward<decltype(args)>(args)...);
};
}
template <auto memFn, class ClassT, class... ArgsT>
auto getCallbackRedundant(ClassT* obj)
{
return [obj](ArgsT&&... args){
return (obj->*memFn)(std::forward<ArgsT&&>(args)...);
};
}

// Example of use
class Foo {
public:
void bar(size_t& x, const std::string& s) { x=s.size(); }
};
int main() {
Foo f;
auto c1 = getCallbackPtr(&f, &Foo::bar);
size_t x1; c1(x1, "123"); std::cout << "c1:" << x1 << "\n";
auto c2 = getCallbackTemplate<&Foo::bar>(&f);
size_t x2; c2(x2, "123"); std::cout << "c2:" << x2 << "\n";
auto c3 = getCallbackRedundant<&Foo::bar, Foo, size_t&, const std::string&>(&f);
size_t x3; c3(x3, "123"); std::cout << "c3:" << x3 << "\n";
}

问题(简​​而言之)

我想要一个结合了上述三个功能的不同方面的功能:

  • 它应该将成员函数作为编译时模板参数,不像getCallbackPtr()。 .
  • 它的 operator()不应该是模板函数,不像 getCallbackTemplate() .
  • 它的模板参数(成员函数指针除外)应该从函数使用中推断出来,不像getCallbackRedundant()。 .

一些细节

以下是我希望成员函数成为模板参数的原因,尽管我必须承认这些在实践中可能不会产生明显的影响:

  • 优化器可能会直接调用成员函数,而不是通过函数指针。事实上,由于这是唯一调用成员函数的地方,它甚至可能被编译器内联到 lambda 中。
  • 生成的函数对象更小(一个指针而不是一个指针加一个成员函数指针),因此更有可能适合 std::function 的占用空间(小对象优化)。

以下是 getCallbackTemplate() 的问题,它有一个模板化的 operator() :

  • 它不适用于 Visual Studio。这对我来说是一个表演障碍。 (错误是 error C3533: a parameter cannot have a type that contains 'auto' ,引用 template <auto memFn, class ClassT> 。)
  • 如果传入了错误类型的参数,我怀疑它会比非模板化的 operator() 产生更复杂和困惑的编译器错误。 (诚​​然,这只是一种预感)。
  • 模板化 operator()不能接受参数的初始化列表。这对我来说根本不是问题,但我提到它是为了记录在案。

我认为需要推断模板参数的原因相当明确:getCallbackRedundant()过于冗长且难以使用。

这能做到吗?怎么办?

最佳答案

推导参数的一种简单方法是使用部分模板特化。

在此示例中,我通过将非类型成员函数指针转发到自定义仿函数,然后将其返回来解决问题。

部分专注于类型,剩下的就很简单了。

#include <iostream>
#include <string>

template <auto memFnPtr, class memFn>
struct getCallbackTemplate;

template <auto memFnPtr, class Ret, class ClassT, class... Args>
struct getCallbackTemplate<memFnPtr, Ret(ClassT::*)(Args...)>
{
getCallbackTemplate (ClassT* obj) : m_obj(obj) {}

Ret operator()(Args... args) {
return (m_obj->*memFnPtr)(std::forward<Args>(args)...);
}

ClassT* m_obj;
};

template <auto memFn, class ClassT>
auto getCallback(ClassT* obj) {
return getCallbackTemplate<memFn, decltype(memFn)>(obj);
}

class Foo {
public:
void bar(std::size_t& x, const std::string& s) { x=s.size(); }
};

int main() {
Foo f;
auto c1 = getCallback<&Foo::bar>(&f);
size_t x1; c1(x1, "123"); std::cout << "c1:" << x1 << "\n";
}

关于lambda - 将 lambda 参数完美转发到成员函数,其中成员函数是非类型模板参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50724636/

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