嵌套绑定表达式

发布于 2024-08-30 10:07:36 字数 1469 浏览 5 评论 0 原文

这是我之前的问题的后续问题。

#include <functional>

int foo(void) {return 2;}

class bar {
public:
    int operator() (void) {return 3;};
    int something(int a) {return a;};
};

template <class C> auto func(C&& c) -> decltype(c()) { return c(); }

template <class C> int doit(C&& c) { return c();}

template <class C> void func_wrapper(C&& c) { func( std::bind(doit<C>, std::forward<C>(c)) ); }

int main(int argc, char* argv[])
{
    // call with a function pointer
    func(foo);
    func_wrapper(foo);  // error

    // call with a member function
    bar b;
    func(b);
    func_wrapper(b);

    // call with a bind expression
    func(std::bind(&bar::something, b, 42));
    func_wrapper(std::bind(&bar::something, b, 42)); // error

    // call with a lambda expression
    func( [](void)->int {return 42;} );
    func_wrapper( [](void)->int {return 42;} );

    return 0;
}

我在 C++ 标头深处遇到编译错误:

功能:1137:错误:从“int (*)()”类型的表达式对“int (&)()”类型的引用进行无效初始化
功能:1137:错误:从“int”转换为非标量类型“std::_Bind(bar, int)>”已请求

func_wrapper(foo) 应该执行 func(doit(foo))。在实际代码中,它将函数打包以供线程执行。 func 是另一个线程执行的函数,doit 位于两者之间,检查未处理的异常并进行清理。但是 func_wrapper 中的额外绑定把事情弄乱了......

This is a followup question to my previous question.

#include <functional>

int foo(void) {return 2;}

class bar {
public:
    int operator() (void) {return 3;};
    int something(int a) {return a;};
};

template <class C> auto func(C&& c) -> decltype(c()) { return c(); }

template <class C> int doit(C&& c) { return c();}

template <class C> void func_wrapper(C&& c) { func( std::bind(doit<C>, std::forward<C>(c)) ); }

int main(int argc, char* argv[])
{
    // call with a function pointer
    func(foo);
    func_wrapper(foo);  // error

    // call with a member function
    bar b;
    func(b);
    func_wrapper(b);

    // call with a bind expression
    func(std::bind(&bar::something, b, 42));
    func_wrapper(std::bind(&bar::something, b, 42)); // error

    // call with a lambda expression
    func( [](void)->int {return 42;} );
    func_wrapper( [](void)->int {return 42;} );

    return 0;
}

I'm getting a compile errors deep in the C++ headers:

functional:1137: error: invalid initialization of reference of type ‘int (&)()’ from expression of type ‘int (*)()’
functional:1137: error: conversion from ‘int’ to non-scalar type ‘std::_Bind<std::_Mem_fn<int (bar::*)(int)>(bar, int)>’ requested

func_wrapper(foo) is supposed to execute func(doit(foo)). In the real code it packages the function for a thread to execute. func would the function executed by the other thread, doit sits in between to check for unhandled exceptions and to clean up. But the additional bind in func_wrapper messes things up...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

燕归巢 2024-09-06 10:07:36

首先介绍一下2个要点:

  • a:使用嵌套的std::bind时,首先对内部的std::bind进行求值,然后将返回值代入当评估外部 std::bind 时它的位置。这意味着 std::bind(f, std::bind(g, _1))(x) 的执行方式与 f(g(x)) 的执行方式相同。如果外部 std::bind 需要函子而不是返回值,则内部 std::bind 应该被 std::ref 包装。

  • b:使用 std::bind 无法将右值引用正确转发到函数。 原因已经详细说明。

那么,我们来看看这个问题。这里最重要的函数可能是 func_wrapper ,它的目的是执行 3 个目的:

  1. 首先完美地将函子转发到 doit 函数模板,
  2. 然后使用 std::bind 将 doit 作为闭包,
  3. 并让 func 函数模板执行返回的函子std::bind 最后。

根据b点,目的1无法实现。因此,让我们忘记完美转发,doit 函数模板必须接受左值引用参数。

根据a点,目的2将通过使用std::ref来执行。

结果,最终的版本可能是:

#include <functional>

int foo(void) {return 2;}

class bar {
public:
    int operator() (void) {return 3;};
    int something(int a) {return a;};
};

template <class C> auto func(C&& c) -> decltype(c()) { return c(); }

template <class C> int doit(C&/*&*/ c)    // r-value reference can't be forwarded via std::bind
{
    return c();
}

template <class C> void func_wrapper(C&& c)
{
    func(std::bind(doit<C>,
                   /* std::forward<C>(c) */ // forget pefect forwarding while using std::bind
                   std::ref(c)) // try to pass the functor itsself instead of its return value
        );
}

int main(int argc, char* argv[])
{
    // call with a function pointer
    func(foo);
    func_wrapper(foo);  // error disappears

    // call with a member function
    bar b;
    func(b);
    func_wrapper(b);

    // call with a bind expression
    func(std::bind(&bar::something, b, 42));
    func_wrapper(std::bind(&bar::something, b, 42)); // error disappears

    // call with a lambda expression
    func( [](void)->int {return 42;} );
    func_wrapper( [](void)->int {return 42;} );

    return 0;
}

但是,如果你真的想达到目的1和2,怎么做呢?试试这个:

#include <functional>
#include <iostream>

void foo()
{
}

struct bar {
    void operator()() {}
    void dosomething() {}
};

static bar b;

template <typename Executor>
void run(Executor&& e)
{
    std::cout << "r-value reference forwarded\n";
    e();
}

template <typename Executor>
void run(Executor& e)
{
    std::cout << "l-value reference forwarded\n";
    e();
}

template <typename Executor>
auto func(Executor&& e) -> decltype(e())
{
    return e();
}

template <bool b>
struct dispatcher_traits {
    enum { value = b };
};

template <typename Executor, bool is_lvalue_reference>
class dispatcher {
private:
    static void dispatch(Executor& e, dispatcher_traits<true>)
    {
        run(e);
    }

    static void dispatch(Executor& e, dispatcher_traits<false>)
    {
        run(std::ref(e));
    }

public:
    static void forward(Executor& e)
    {
        dispatch(e, dispatcher_traits<is_lvalue_reference>());
    }
};

template <typename Executor>
void func_wrapper(Executor&& e)
{
    typedef dispatcher<Executor,
                       std::is_lvalue_reference<Executor>::value>
        dispatcher_type;

    func(std::bind(&dispatcher_type::forward, std::ref(e)));
}

int main()
{
    func_wrapper(foo);   // l-value
    func_wrapper(b);  // l-value
    func_wrapper(bar());  // r-value
    func_wrapper(std::bind(&bar::dosomething, &b));  // r-value
    func_wrapper([](){});  // r-value
}

让我解释一些要点:

  • 为了减少大量返回语句,将函子签名从 int() 更改为 void()。
  • 2个run()函数模板用于检查原始函子参数是否完美转发。
  • Dispatcher_traits 会将 bool 常量映射到类型。
  • 您最好将 Dispatcher::forward 命名为与 Dispatcher::dispatch 不同,否则您必须使用 Dispatcher::forward 的签名调用 std::bind 模板。

At the beginning, please let me introduce 2 key points:

  • a: When using nested std::bind, the inner std::bind is evaluated first, and the return value will be substituted in its place while the outer std::bind is evaluated. That means std::bind(f, std::bind(g, _1))(x) executes as same as f(g(x)) does. The inner std::bind is supposed to be wrapped by std::ref if the outer std::bind wants a functor rather than a return value.

  • b: The r-value reference cannot be correctly forwarded to the function by using std::bind. And the reason has already been illustrated in detail.

So, let's look at the question. The most importance function here might be func_wrapper which is intended to perform 3 purposes:

  1. Perfect forwarding a functor to doit function template at first,
  2. then using std::bind to make doit as a closure,
  3. and letting func function template execute the functor returned by std::bind at last.

According to point b, purpose 1 cannot be fulfilled. So, let's forget perfect forwarding and doit function template has to accept a l-value reference parameter.

According to point a, purpose 2 will be performed by using std::ref.

As a result, the final version might be:

#include <functional>

int foo(void) {return 2;}

class bar {
public:
    int operator() (void) {return 3;};
    int something(int a) {return a;};
};

template <class C> auto func(C&& c) -> decltype(c()) { return c(); }

template <class C> int doit(C&/*&*/ c)    // r-value reference can't be forwarded via std::bind
{
    return c();
}

template <class C> void func_wrapper(C&& c)
{
    func(std::bind(doit<C>,
                   /* std::forward<C>(c) */ // forget pefect forwarding while using std::bind
                   std::ref(c)) // try to pass the functor itsself instead of its return value
        );
}

int main(int argc, char* argv[])
{
    // call with a function pointer
    func(foo);
    func_wrapper(foo);  // error disappears

    // call with a member function
    bar b;
    func(b);
    func_wrapper(b);

    // call with a bind expression
    func(std::bind(&bar::something, b, 42));
    func_wrapper(std::bind(&bar::something, b, 42)); // error disappears

    // call with a lambda expression
    func( [](void)->int {return 42;} );
    func_wrapper( [](void)->int {return 42;} );

    return 0;
}

But, if you really want to achieve purpose 1 and 2, how? Try this:

#include <functional>
#include <iostream>

void foo()
{
}

struct bar {
    void operator()() {}
    void dosomething() {}
};

static bar b;

template <typename Executor>
void run(Executor&& e)
{
    std::cout << "r-value reference forwarded\n";
    e();
}

template <typename Executor>
void run(Executor& e)
{
    std::cout << "l-value reference forwarded\n";
    e();
}

template <typename Executor>
auto func(Executor&& e) -> decltype(e())
{
    return e();
}

template <bool b>
struct dispatcher_traits {
    enum { value = b };
};

template <typename Executor, bool is_lvalue_reference>
class dispatcher {
private:
    static void dispatch(Executor& e, dispatcher_traits<true>)
    {
        run(e);
    }

    static void dispatch(Executor& e, dispatcher_traits<false>)
    {
        run(std::ref(e));
    }

public:
    static void forward(Executor& e)
    {
        dispatch(e, dispatcher_traits<is_lvalue_reference>());
    }
};

template <typename Executor>
void func_wrapper(Executor&& e)
{
    typedef dispatcher<Executor,
                       std::is_lvalue_reference<Executor>::value>
        dispatcher_type;

    func(std::bind(&dispatcher_type::forward, std::ref(e)));
}

int main()
{
    func_wrapper(foo);   // l-value
    func_wrapper(b);  // l-value
    func_wrapper(bar());  // r-value
    func_wrapper(std::bind(&bar::dosomething, &b));  // r-value
    func_wrapper([](){});  // r-value
}

Let me explain some points:

  • To reduce lots of return statements, changing functor signature from int() to void().
  • The 2 run() function templates are used to check whether the original functor parameter is perfect forwarded or not.
  • dispatcher_traits is going to map bool constant to type.
  • You'd better name dispatcher::forward to differ from dispatcher::dispatch or you have to invoke std::bind template with dispatcher::forward's signature.
青春有你 2024-09-06 10:07:36

现在第二次看这个,我想我对你看到的第一个错误有一个合理的解释。

在这种情况下,查看完整的错误和导致该错误的模板实例化会更有帮助。例如,我的编译器(GCC 4.4)打印的错误以以下几行结尾:

test.cpp:12:   instantiated from ‘decltype (c()) func(C&&) [with C = std::_Bind<int (*(int (*)()))(int (&)())>]’
test.cpp:16:   instantiated from ‘void func_wrapper(C&&) [with C = int (&)()]’
test.cpp:22:   instantiated from here
/usr/include/c++/4.4/tr1_impl/functional:1137: error: invalid initialization of reference of type ‘int (&)()’ from expression of type ‘int (*)()’

现在从下往上看,实际的错误消息似乎是正确的;编译器推导的类型不兼容。

第一个模板实例化位于 func_wrapper,清楚地显示了编译器从 func_wrapper(foo) 中的实际参数 foo 推导出来的类型。我个人认为这是一个函数指针,但它实际上是一个函数引用

第二个模板实例化几乎不可读。但是稍微搞了一下 std::bind ,我了解到 GCC 为绑定函子打印的文本表示的格式大致是:

std::_Bind<RETURN-TYPE (*(BOUND-VALUE-TYPES))(TARGET-PARAMETER-TYPES)>

所以把它拆开:

std::_Bind<int (*(int (*)()))(int (&)())>
// Return type: int
// Bound value types: int (*)()
// Target parameter types: int (&)()

这就是不兼容类型开始的地方。显然,即使func_wrapper中的c是一个函数引用,但一旦传递给std::bind<,它就会变成一个函数指针 /code>,导致类型不兼容。就其价值而言,在这种情况下 std::forward 根本不重要。

我的推理是 std::bind 似乎只关心值,而不关心引用。在 C/C++ 中,不存在函数值这样的东西;只有引用和指针。因此,当函数引用被取消引用时,编译器只能给你一个有意义的函数指针。

您对此唯一的控制权是您的模板参数。您必须告诉编译器您正在处理函数指针从一开始才能完成这项工作。无论如何,这可能就是你的想法。为此,请显式指定模板参数 C 所需的类型:

func_wrapper<int (*)()>(foo);

或者更简短的解决方案,显式获取函数的地址:

func_wrapper(&foo); // with C = int (*)()

如果我找出第二个错误,我会回复您。 :)

Looking at this the second time now, and I think I have a plausable explanation for the first error you are seeing.

In this case, it's more helpful to look at the complete error and the template instantiations that lead up to it. The error printed by my compiler (GCC 4.4), for example, ends with the following lines:

test.cpp:12:   instantiated from ‘decltype (c()) func(C&&) [with C = std::_Bind<int (*(int (*)()))(int (&)())>]’
test.cpp:16:   instantiated from ‘void func_wrapper(C&&) [with C = int (&)()]’
test.cpp:22:   instantiated from here
/usr/include/c++/4.4/tr1_impl/functional:1137: error: invalid initialization of reference of type ‘int (&)()’ from expression of type ‘int (*)()’

Now looking at this bottom-up, the actual error message seems correct; the types the compiler has deduced are incompatible.

The first template instantiation, at func_wrapper, clearly shows what type the compiler has deduced from the actual parameter foo in func_wrapper(foo). I personally expected this to be a function pointer, but it is in fact a function reference.

The second template instantiation is hardly readable. But messing around with std::bind a bit, I learned that the format of the textual representation GCC prints for a bind functor is roughly:

std::_Bind<RETURN-TYPE (*(BOUND-VALUE-TYPES))(TARGET-PARAMETER-TYPES)>

So tearing it apart:

std::_Bind<int (*(int (*)()))(int (&)())>
// Return type: int
// Bound value types: int (*)()
// Target parameter types: int (&)()

This is where the incompatible types start. Apparently, even though c in func_wrapper is a function reference, it turns into a function pointer once passed to std::bind, resulting in the type incompatibility. For what it's worth, std::forward doesn't matter at all in this case.

My reasoning here is that std::bind only seems to care about values, and not references. In C/C++, there's no such thing as a function value; there's only references and pointers. So when the function reference is dereferenced, the compiler can only meaningfully give you a function pointer.

The only control you have over this is your template parameters. You will have to tell the compiler that you're dealing with a function pointer from the start to make this work. It's probably what you had in mind anyways. To do that, explicitly specify the type you want for the template parameter C:

func_wrapper<int (*)()>(foo);

Or the more brief solution, explicitly take the function's address:

func_wrapper(&foo); // with C = int (*)()

I'll get back to you if I ever figure out the second error. :)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文