【问题标题】:C++ Bind Return Type Template ArgumentsC++ 绑定返回类型模板参数
【发布时间】:2015-10-23 11:37:39
【问题描述】:

我有以下代码:

#include <functional>

template <typename T>
class TD;   // For displaying type

void f(int, int, int) {  }

int main() {
    auto g = std::bind(f, std::placeholders::_1, 2, 2);
    TD<decltype(g)> td1;
    return 0;
}

在这段代码中,TD 是一个模板技巧,用于显示通过 decltype 传递的模板参数。

编译器的输出如下(在C++14模式下编译):

prog.cpp: In function 'int main()':
prog.cpp:10:18: error: aggregate 'TD<std::_Bind<void (*(std::_Placeholder<1>, int, int))(int, int, int)> > td1' has incomplete type and cannot be defined
  TD<decltype(g)> td1;
                  ^

嗯,不完整的类型错误当然除外。但是让我对这个错误消息感到好奇的是std::_Bind&lt;void (*(std::_Placeholder&lt;1&gt;, int, int))(int, int, int)&gt;。我可以理解std::_Bind 是一个代理类,它定义了operator() 并使我们的目的成为可能。但它的模板参数void (*(std::_Placeholder&lt;1&gt;, int, int))(int, int, int) 让我惊叹!我应该如何解释它?它在用户域代码中有什么用处吗?如何使用此声明创建自己的类?

【问题讨论】:

    标签: c++ function templates c++11 c++14


    【解决方案1】:
    void (*(std::_Placeholder<1>, int, int))(int, int, int)
    

    这声明了一个未命名的函数,它接受三个参数(std::_Placeholder&lt;1&gt;intint)并返回一个指向函数的指针,该函数接受三个 ints 并返回 void

    让我们稍微简化一下。首先考虑一个简单的函数声明:

    void f(int)
    

    现在,在(函数或模板的)参数声明中,您可以省略名称并得到 ​​p>

    void (int) 
    

    如果在函数声明的参数列表中使用,它将等效于函数指针void(*)(int)

    返回函数指针的函数声明如下:

    void (*f(int))(int);
    //   ^       ^         <- this pair of parentheses changes
    //                        the order in which the declaration is parsed.
    //                        Without it, the return type would be `void*`
    //                        and you'd get a syntax error
    

    现在您可以删除名称 f 并且您基本上得到了您所询问的相同内容。

    它的用途?显然它在实现std::bind 时很有用:) 我现在想不出其他任何东西......

    【讨论】:

    • 哇!我怎么忘了应用那个简单的类型声明阅读! :) 现在只剩下一件事了。如果您能详细说明 'std::bind' 如何使用它,那就太好了! :)
    • @jnbrq-CanberkSönmez 如果您正在考虑使用_Placeholder,请知道它是非标准的。您将使用仅在此特定版本的编译器中定义的类型。它甚至不能保证在编译器的未来版本中工作。
    【解决方案2】:

    C++11 标志着 function 的出现,这使得定义函数指针变得更加容易:

    std::function 的实例可以存储、复制和调用任何 Callable 目标——函数、lambda 表达式、绑定表达式或其他函数对象,以及指向成员函数的指针和指向数据成员的指针。

    例如,假设您需要在其中一个函数中接收指向string foo(int param) { return to_string(param); } 的函数指针。在 C++11 之前,您的函数需要如下所示:

    void bar(string (*func)(int)) { cout << func(13) << endl; }
    

    让我们更进一步,假设您想将foo 扩展为:string foo2(int lhs, int rhs) { return to_string(lhs + rhs); }。但现在你想把它塞回bar。这样做bar(bind(&amp;foo2, placeholders::_1, 42)); 会给你这样的错误:

    无法转换 'std::_Bind_helper (*)(int, int), const std::_Placeholder&, int>::type {aka std::_Bind (*(std::_Placeholder , int))(int, int)>}' 到 'std::string (*)(int) {aka std::basic_string (*)(int)}' 用于参数 '1' 到 'void bar(std: :string (*)(int))'

    您可以通过创建一个采用特定于实现的参数的函数来解决此错误,例如:void bar2(_Bind&lt;string (*(_Placeholder&lt;1&gt;, int))(int, int)&gt; func) { cout &lt;&lt; func(13) &lt;&lt; endl; },它可以成功地调用为:bar2(bind(&amp;foo2, placeholders::_1, 42));。这是特定于实现的原因是类型:_Bind_Placeholder 是非标准的。其实bind的返回是:

    一个未指定类型T的函数对象

    这将我们带到function。如果您还没有被函数指针的语法限制关闭,您需要使用function 参数来接受由bind 创建的对象。让我们使用function 创建一个新的bar

    void bar3(function<string(int)> func) { cout << func(13) << endl; }
    

    这能够接受传统的函数指针和bind 函子。此外,它可以处理 lambda,因此您可以这样做:bar3([](int param) { return to_string(param); });

    我创建了一个live example,因此您可以尝试一下,希望function 对象的好处是显而易见的。

    【讨论】:

    • 嗯,实际上并不完全是我需要知道的。我的问题主要是void (*(std::_Placeholder&lt;1&gt;, int, int))(int, int, int)的意思和可以用在哪里。
    • @jnbrq-CanberkSönmez 实际上,这确实解决了如何使用bind 的返回,只是没有具体说明为什么应该使用function&lt;void(int, int)&gt; 而不是_Bind&lt;void (*(_Placeholder&lt;1&gt;, int, int))(int, int, int)&gt; 我已经编辑了回答解释。 tldr:bind 返回的类型是特定于实现的。您必须使用 function 对象来接受它。
    • 除非您确实需要类型擦除,否则最好使用函数模板并按原样接受函数对象。
    • @T.C.我觉得你在这里给了我一个有价值的信息,但我不明白。您能澄清一下您所说的更好的方法吗?
    • template&lt;class F&gt; void bar4(F func) { cout &lt;&lt; func(13) &lt;&lt; endl; },甚至template&lt;class F&gt; void bar4(F&amp;&amp; func) { cout &lt;&lt; std::forward&lt;F&gt;(func)(13) &lt;&lt; endl; }
    猜你喜欢
    • 1970-01-01
    • 2012-07-02
    • 1970-01-01
    • 1970-01-01
    • 2020-10-20
    • 1970-01-01
    • 2022-01-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多