UFO ET IT

C ++ 14에서 람다를 통해 std :: bind를 사용하는 이유는 무엇입니까?

ufoet 2020. 12. 30. 08:30
반응형

C ++ 14에서 람다를 통해 std :: bind를 사용하는 이유는 무엇입니까?


11 ++ C 전에 내가 사용 boost::bind또는 boost::lambda많이. bind부분은 표준 라이브러리 ( std::bind) 로 만들었고 다른 부분은 핵심 언어 (C ++ 람다)의 일부가되었고 람다 사용을 훨씬 더 쉽게 만들었습니다. 요즘에는 std::bindC ++ 람다로 거의 모든 것을 할 수 있기 때문에 거의 사용 하지 않습니다. std::bind내가 생각할 수있는 유효한 사용 사례가 하나 있습니다.

struct foo
{
  template < typename A, typename B >
  void operator()(A a, B b)
  {
    cout << a << ' ' << b;
  }
};

auto f = bind(foo(), _1, _2);
f( "test", 1.2f ); // will print "test 1.2"

이에 해당하는 C ++ 14는 다음과 같습니다.

auto f = []( auto a, auto b ){ cout << a << ' ' << b; }
f( "test", 1.2f ); // will print "test 1.2"

훨씬 짧고 간결합니다. (C ++ 11에서는 자동 매개 변수로 인해 아직 작동하지 않습니다.) std::bindC ++ 람다 대안 이기는 다른 유효한 사용 사례 std::bind있습니까? 아니면 C ++ 14에 불필요합니까?


Scott Meyers가 이에 대해 이야기 했습니다. 이것이 내가 기억하는 것입니다.

C ++ 14에는 람다로도 할 수없는 유용한 바인드가 없습니다.

그러나 C ++ 11 에서는 람다로 수행 할 수없는 몇 가지 작업이 있습니다.

  1. 람다를 만들 때 캡처하는 동안 변수를 이동할 수 없습니다. 변수는 항상 lvalue로 캡처됩니다. 바인드의 경우 다음과 같이 작성할 수 있습니다.

    auto f1 = std::bind(f, 42, _1, std::move(v));
    
  2. 식은 캡처 할 수 없으며 식별자 만 캡처 할 수 있습니다. 바인드의 경우 다음과 같이 작성할 수 있습니다.

    auto f1 = std::bind(f, 42, _1, a + b);
    
  3. 함수 개체에 대한 인수 오버로딩. 이것은 이미 질문에서 언급되었습니다.

  4. 완벽하게 전진하는 것은 불가능하다

C ++ 14 에서는 이 모든 것이 가능합니다.

  1. 이동 예 :

    auto f1 = [v = std::move(v)](auto arg) { f(42, arg, std::move(v)); };
    
  2. 식 예 :

    auto f1 = [sum = a + b](auto arg) { f(42, arg, sum); };
    
  3. 질문보기

  4. 완벽한 전달 : 당신은 쓸 수 있습니다

    auto f1 = [=](auto&& arg) { f(42, std::forward<decltype(arg)>(arg)); };
    

바인딩의 몇 가지 단점 :

  • 바인딩은 이름으로 바인딩하므로 결과적으로 동일한 이름 (오버로드 된 함수)을 가진 여러 함수가있는 경우 bind는 사용할 함수를 알지 못합니다. 다음 예제는 컴파일되지 않지만 람다는 문제가 없습니다.

    void f(int); void f(char); auto f1 = std::bind(f, _1, 42);
    
  • 바인드 함수를 사용할 때 인라인 될 가능성이 적습니다.

반면에 람다는 이론적으로 bind보다 더 많은 템플릿 코드를 생성 할 수 있습니다. 각 람다에 대해 고유 한 유형을 얻습니다. bind의 경우 다른 인수 유형과 다른 함수가있을 때만 발생합니다 (실제로 동일한 인수와 함수로 여러 번 바인딩하는 경우는 자주 발생하지 않습니다).

Jonathan Wakely가 그의 답변에서 언급 한 것은 실제로 bind를 사용하지 않는 또 다른 이유입니다. 나는 당신이 왜 조용히 논쟁을 무시하고 싶은지 알 수 없습니다.


std::bind 다형성 람다가 할 수없는 일을 여전히 할 수 있습니다 : 오버로드 된 함수 호출

struct F {
  bool operator()(char, int);
  std::string operator()(char, char);
};

auto f = std::bind(F(), 'a', std::placeholders::_1);
bool b = f(1);
std::string s = f('b');

bind 식에 의해 생성 된 호출 래퍼는 제공하는 인수에 따라 다른 함수를 호출하고 C ++ 14 다형성 람다의 클로저는 다른 유형 의 인수를 사용할 수 있지만 다른 의 인수를 사용할 수 없으며 항상 ( 전문화) 클로저에 대한 동일한 기능. 수정 : 아래 설명 참조

에서 반환 된 래퍼는 너무 많은 인수를 사용하여 std::bind호출 할 수도 있으며 무시할 수 있지만 람다에 의해 생성 된 클로저는 너무 많은 인수를 전달하려는 시도를 진단합니다 ...하지만 이점은 고려하지 않습니다. :)std::bind


For me, a valid use for std::bind is to make it clear that I'm using a member function as a predicate. That is, if all I do is call a member function, it's bind. If I do extra stuff with the argument (besides calling a memeber function), it's a lambda:

using namespace std;
auto is_empty = bind(&string::empty, placeholders::_1); // bind = just map member
vector<string> strings;
auto first_empty = any_of(strings.begin(), strings.end(), is_empty);

auto print_non_empty = [](const string& s) {            // lambda = more than member
    if(s.empty())                // more than calling empty
        std::cout << "[EMPTY]";  // more than calling empty
    else                         // more than calling empty
        std::cout << s;          // more than calling empty
};
vector<string> strings;
for_each(strings.begin(), strings.end(), print_non_empty);

Sometimes it is just less code. Consider this:

bool check(int arg1, int arg2, int arg3)
{
  return ....;
}

Then

wait(std::bind(check,a,b,c));

vs lambda

wait([&](){return check(a,b,c);});

I think that bind is easier to read here compared to the lambda which looks like a https://en.wikipedia.org/wiki/Brainfuck


Another difference is that arguments to bind must be copied or moved, while a lambda can use variables captured by reference. See example below:

#include <iostream>
#include <memory>

void p(const int& i) {
    std::cout << i << '\n';
}

int main()
{
    std::unique_ptr<int> f = std::make_unique<int>(3);

    // Direct
    p(*f);

    // Lambda ( ownership of f can stay in main )
    auto lp = [&f](){p(*f);};
    lp();

    // Bind ( does not compile - the arguments to bind are copied or moved)
    auto bp = std::bind(p, *f, std::placeholders::_1);
    bp();
}

Not sure if it's possible to workaround the issue to use bind above without changing the signature of void p(const int&).


Just expanding @BertR's comment to this answer to something testable, though I confess I couldn't quite get a solution using std::forward<> to work.

#include <string>
#include <functional>
using namespace std::string_literals;

struct F {
    bool        operator()(char c, int  i) { return c == i;  };
    std::string operator()(char c, char d) { return ""s + d; };
};

void test() {
    { // using std::bind
        auto f = std::bind(F(), 'a', std::placeholders::_1);
        auto b = f(1);
        auto s = f('b');
    }
    { // using lambda with parameter pack
        auto x = [](auto... args) { return F()('a', args...); };
        auto b = x(1);
        auto s = x('b');
    }
}

Test at Compiler Explorer

ReferenceURL : https://stackoverflow.com/questions/17363003/why-use-stdbind-over-lambdas-in-c14

반응형