2

I have a situation where I'm stuck with a C++11 only compiler (gcc 4.7.2, so I don't have access to C++14 generalized lambda capture), so I use std::bind as a work-around if I need to capture a move-only object.

I read about that workaround from this stackoverflow answer, and I've had a lot of success using this technique.

However, I have a situation where I form a higher-order functor by forwarding a bind expression as a bound argument of another bind expression.

I can simplify it to an example. The example is contrived and simplified for the purpose of this question:

template <class F>
void func(F&& func)
{
    auto f = std::bind(
        [](F& f) -> void
        {

        },
        std::forward<F>(func)
    );

    f(); // causes compiler error
}

int main()
{
    func(
        std::bind(
            []() { }
        )
    );
}

For a reason which I cannot understand, the compiler generates a whole slew of errors when I call f(). Rather than paste the entire error message, I'll try to simplify it:

test7.cpp:22:2: error: no match for call to ‘(std::_Bind<func(F&&) [with F = std::_Bind<main()::<lambda()>()>]::<lambda(std::_Bind<main()::<lambda()>()>)>(std::_Bind<main()::<lambda()>()>)>) ()’

This error is generated from GCC 4.7.2.

I see no reason why this should happen. In the contrived example above, you'll notice that in main() I have an extra bind expression around an empty lambda. If I remove the bind expression, and simply pass the empty lambda, like:

func([]() { });

... then no error occurs. But again, this is a contrived example - in the actual application, I need to wrap the lambda in a bind expression for the purpose of move capture. And I see no reason why [](){} is semantically different from std::bind([](){}). They should both be functionally equivalent, even though they evaluate to different types.

So, why does this error occur? Or is this simply a problem with GCC? (I realize 4.7.2 is a bit old at this point.)

Community
  • 1
  • 1
Siler
  • 8,976
  • 11
  • 64
  • 124

1 Answers1

1

When one of the arguments to bind(f, args...) is the result of a call to bind(g, args...), the argument passed to f is the result of evaluating g (See [func.bind.bind]/10, specifically bullet 10.2). In the case of your program, the result of evaluating the lambda [](){} is passed as the first parameter to [](F& f) which is of course ill-formed.

Moderately ugly solution: introduce a wrapper class for the inner bind expression that implicitly converts to reference-to-wrapped type:

template <class F>
class protect_bind_expression {
    F f_;
public:
    protect_bind_expression(F f) :
        f_{std::move(f)} {}

    operator F&() { return f_; }
};

template <class F>
protect_bind_expression<typename std::decay<F>::type>
protect(F&& f) {
    return {std::forward<F>(f)};
}

template <class F>
void func(F&& func)
{
    auto f = std::bind(
        [](F&){},
        protect(std::forward<F>(func))
    );

    f(); // Doesn't cause compiler error
}
Casey
  • 41,449
  • 7
  • 95
  • 125
  • Wait, so `bind` specifically does function composition as a "feature" if you pass a `bind` expression to another bind expression? – Siler Oct 24 '15 at 15:25
  • @Siler Yes, exactly. And no, I don't know how to avoid the "feature." – Casey Oct 24 '15 at 15:27