【问题标题】:How to bind variadic template params to function如何将可变参数模板参数绑定到函数
【发布时间】:2023-03-16 07:40:01
【问题描述】:

我正在尝试模仿 std::thread 构造函数功能:

template< class Function, class... Args > 
explicit thread( Function&& f, Args&&... args );

我尝试使用调试器单步执行以查看它是如何工作的,但我无法弄清楚。

如何像线程的构造函数一样创建和存储绑定类型?

类似这样的东西(语法可能错误):

class myClass{
private:
auto bindType;

public:
template< class Function, class... Args > 
explicit myClass( Function&& f, Args&&... args ) : bindType(somehowBind(f, args) {}
void evaluate() {bindType();}
};

使用示例:

int test(int i) {return i;}

int main(){
myClass my(test, 5);
my.evaluate();
}

请注意,我不在乎 somehowBind 函数是否会忽略返回类型,即它的返回类型可以是 std::function 之类的东西。 我不想做的就是了解如何将class... Args 绑定到给定的函数f,这样在调用somehowBind 之后它就会像std::bind 那样工作。 为了澄清我的观点,您可以考虑一下我想要达到的目标:

thread t(test, 5); // unlike the usual std:::thread, this one is created in suspended mode therefore I need somehow to bind `f` with `5` and store it
t.start(); // now t is executed

这有点像 C# 和 Java 线程,它们不是在构造后立即执行的。

【问题讨论】:

  • 我认为您需要多解释一下您想要实现的目标。
  • 在包装代码层的深处会有一个std::tuple 存储可变参数、指针或对它们的引用。正如 Tomasz 所说,请详细说明您的包装代码应该是什么样的。
  • 您在寻找std::function 吗? class myClass { std::function&lt;void()&gt; m; public: template&lt;class F, class... Args&gt; myClass(F&amp;&amp; f, Args&amp;&amp;... args) : m(std::bind(std::forward&lt;F&gt;(f), std::forward&lt;Args&gt;(args)...)) {} void evaluate() {m();} };
  • @dyp 是的,我正在寻找的那个,我不知道您可以将这样的语法与 std::bind 一起使用(转发 ... 绑定)。我不知道为什么,但在 std::thread 中,它们太复杂了。
  • @dyp 得到的std::bind表达式函子的返回值可能不是void

标签: c++ c++11


【解决方案1】:

对于初学者来说,使用std::bind 将一些参数绑定到一个函数你很简单:

// Some function.
void printValues(int x, double y) {
    std::cout << x << " " << y << std::endl;
}

auto func = std::bind(printValues, 5, 2.0); // Bind params and return functor.
func(); // Evaluate function call (returns void in this case).

接下来,要将仿函数及其参数存储在一个类中,并且在评估时不关心返回值,然后只需使用 lambda 表达式包装 std::bind 表达式(lambda 用于删除返回值):

struct Foo {
    template <typename Function, typename... Args>
    Foo(Function&& func, Args&&... args) {
        auto f = std::bind(std::forward<Function>(func), std::forward<Args>(args)...);
        func_ = [f] { f(); };
        // func_ = [f{std::move(f)}] { f(); }; // In C++14 you can move capture.
    }
    void evaluate() { func_(); }
    std::function<void()> func_;
};

Also see this live example

如果您要存储可变参数包,请查看以下答案:How to store variadic template arguments?

【讨论】:

  • 正如我在对 OP 的评论中所说,bind 函子的返回值可转换为 void。您不需要将其包装在 lambda 中。 Live example
  • @dyp 但我认为bind 函子的返回值是作为参数传递给它的可调用函数的返回值?例如。 int 不能隐式转换为 void?我只是想得到这个。
  • 嗯,现在它变得有趣了。似乎类型不是隐式的,而是只能显式转换为voidstd::function 的要求似乎需要通过INVOKE 进行隐式 转换,但std::function::operator() 包含void 返回类型的特殊情况。 libstdc++ 的std::function 实现包含一个特殊情况来检查传递的实体是否可调用,即如果目标返回类型是void,它接受可调用的任何返回类型。 IE。这要么是标准中的错误(很容易修复),要么是 libstdc++ 扩展。
  • 看来你是对的,这已被故意更改为允许通过 SFINAE 支持重载解析消歧。见stackoverflow.com/a/9343400/420683
  • @dyp 确实很有趣。我找到了同一个帖子。显然您的代码无法在 libc++ 上编译,但它确实可以在 libstdc++ 上运行。
猜你喜欢
  • 2011-11-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-20
  • 2016-12-01
  • 1970-01-01
  • 2021-10-15
  • 1970-01-01
相关资源
最近更新 更多