람다를 함수 포인터로 전달 not usable in a

람다 함수를 함수 포인터로 전달할 수 있습니까? 그렇다면 컴파일 오류가 발생하여 잘못된 것을 수행해야합니다.

다음 예를 고려하십시오

using DecisionFn = bool(*)();

class Decide
{
public:
    Decide(DecisionFn dec) : _dec{dec} {}
private:
    DecisionFn _dec;
};

int main()
{
    int x = 5;
    Decide greaterThanThree{ [x](){ return x > 3; } };
    return 0;
}

나는이 때 이를 컴파일하려고 , 나는 다음과 같은 컴파일 오류가 발생합니다 :

In function 'int main()':
17:31: error: the value of 'x' is not usable in a constant expression
16:9:  note: 'int x' is not const
17:53: error: no matching function for call to 'Decide::Decide(<brace-enclosed initializer list>)'
17:53: note: candidates are:
9:5:   note: Decide::Decide(DecisionFn)
9:5:   note: no known conversion for argument 1 from 'main()::<lambda()>' to 'DecisionFn {aka bool (*)()}'
6:7:   note: constexpr Decide::Decide(const Decide&)
6:7:   note: no known conversion for argument 1 from 'main()::<lambda()>' to 'const Decide&'
6:7:   note: constexpr Decide::Decide(Decide&&)
6:7:   note: no known conversion for argument 1 from 'main()::<lambda()>' to 'Decide&&'

그것은 소화 해야하는 오류 메시지 중 하나입니다. 그러나 내가 얻는 것은 람다를 처리 constexpr할 수 없으므로 함수 포인터로 전달할 수 없다는 것입니다. xconst도 만들려고했지만 도움이되지 않는 것 같습니다.



답변

람다는 C ++ 11 표준 섹션 5.1.2 [expr.prim.lambda]에서 ( emphasis mine ) 초안 에서 캡처하지 않으면 함수 포인터로만 변환 할 수 있습니다 .

람다 캡처없는 람다 식 의 클로저 형식에는 클로저 형식의 함수 호출 연산자와 동일한 매개 변수 및 반환 형식을 가진 함수를 가리키는 공개 비 가상적 비명 시적 const 변환 함수가 있습니다. 이 변환 함수에 의해 리턴 된 값은 호출 될 때 클로저 유형의 함수 호출 연산자를 호출하는 것과 동일한 효과를 갖는 함수의 주소입니다.

cppreference는 Lambda 함수 에 대한 섹션에서도이를 다루고 있습니다 .

따라서 다음 대안이 작동합니다.

typedef bool(*DecisionFn)(int);

Decide greaterThanThree{ []( int x ){ return x > 3; } };

그리고 이것도 :

typedef bool(*DecisionFn)();

Decide greaterThanThree{ [](){ return true ; } };

와 같은 5gon12eder의 포인트 아웃, 당신은 또한 사용할 수 std::function있지만, 참고 std::function무거운 무게를 그 비용 덜 상충되지 않도록.


답변

Shafik Yaghmour의 답변 은 람다가 캡처가있는 경우 함수 포인터로 전달할 수없는 이유를 올바르게 설명합니다. 문제에 대한 두 가지 간단한 수정 사항을 보여 드리고자합니다.

  1. std::function원시 함수 포인터 대신 사용하십시오 .

    이것은 매우 깨끗한 솔루션입니다. 그러나 유형 삭제에 대한 추가 오버 헤드 (가상 함수 호출)가 포함되어 있습니다.

    #include <functional>
    #include <utility>
    
    struct Decide
    {
      using DecisionFn = std::function<bool()>;
      Decide(DecisionFn dec) : dec_ {std::move(dec)} {}
      DecisionFn dec_;
    };
    
    int
    main()
    {
      int x = 5;
      Decide greaterThanThree { [x](){ return x > 3; } };
    }
    
  2. 아무것도 캡처하지 않는 람다 식을 사용하십시오.

    술어는 실제로 부울 상수이므로 다음은 현재 문제를 빠르게 해결할 수 있습니다. 왜 그리고 어떻게 작동하는지에 대한 좋은 설명 은 이 답변 을 참조하십시오 .

    // Your 'Decide' class as in your post.
    
    int
    main()
    {
      int x = 5;
      Decide greaterThanThree {
        (x > 3) ? [](){ return true; } : [](){ return false; }
      };
    }
    

답변

람다 식, 심지어 캡처 된 식은 함수 포인터 (멤버 ​​함수의 포인터)로 처리 될 수 있습니다.

람다 식은 단순한 함수가 아니기 때문에 까다 롭습니다. 실제로는 operator ()가있는 객체입니다.

당신이 창의적이라면 이것을 사용할 수 있습니다! std :: function 스타일의 “function”클래스를 생각해보십시오. 객체를 저장하면 함수 포인터를 사용할 수도 있습니다.

함수 포인터를 사용하려면 다음을 사용할 수 있습니다.

int first = 5;
auto lambda = [=](int x, int z) {
    return x + z + first;
};
int(decltype(lambda)::*ptr)(int, int)const = &decltype(lambda)::operator();
std::cout << "test = " << (lambda.*ptr)(2, 3) << std::endl;

“std :: function”처럼 작동을 시작할 수있는 클래스를 만들려면 먼저 객체와 함수 포인터를 저장할 수있는 것보다 클래스 / 구조가 필요합니다. 또한 그것을 실행하려면 operator ()가 필요합니다.

// OT => Object Type
// RT => Return Type
// A ... => Arguments
template<typename OT, typename RT, typename ... A>
struct lambda_expression {
    OT _object;
    RT(OT::*_function)(A...)const;

    lambda_expression(const OT & object)
        : _object(object), _function(&decltype(_object)::operator()) {}

    RT operator() (A ... args) const {
        return (_object.*_function)(args...);
    }
};

이를 통해 원본을 사용하는 것처럼 캡처되지 않은 캡처 된 람다를 실행할 수 있습니다.

auto capture_lambda() {
    int first = 5;
    auto lambda = [=](int x, int z) {
        return x + z + first;
    };
    return lambda_expression<decltype(lambda), int, int, int>(lambda);
}

auto noncapture_lambda() {
    auto lambda = [](int x, int z) {
        return x + z;
    };
    return lambda_expression<decltype(lambda), int, int, int>(lambda);
}

void refcapture_lambda() {
    int test;
    auto lambda = [&](int x, int z) {
        test = x + z;
    };
    lambda_expression<decltype(lambda), void, int, int>f(lambda);
    f(2, 3);

    std::cout << "test value = " << test << std::endl;
}

int main(int argc, char **argv) {
    auto f_capture = capture_lambda();
    auto f_noncapture = noncapture_lambda();

    std::cout << "main test = " << f_capture(2, 3) << std::endl;
    std::cout << "main test = " << f_noncapture(2, 3) << std::endl;

    refcapture_lambda();

    system("PAUSE");
    return 0;
}

이 코드는 VS2015와 함께 작동합니다

04.07.17 업데이트 :

template <typename CT, typename ... A> struct function
: public function<decltype(&CT::operator())(A...)> {};

template <typename C> struct function<C> {
private:
    C mObject;

public:
    function(const C & obj)
        : mObject(obj) {}

    template<typename... Args> typename
    std::result_of<C(Args...)>::type operator()(Args... a) {
        return this->mObject.operator()(a...);
    }

    template<typename... Args> typename
    std::result_of<const C(Args...)>::type operator()(Args... a) const {
        return this->mObject.operator()(a...);
    }
};

namespace make {
    template<typename C> auto function(const C & obj) {
        return ::function<C>(obj);
    }
}

int main(int argc, char ** argv) {
   auto func = make::function([](int y, int x) { return x*y; });
   std::cout << func(2, 4) << std::endl;
   system("PAUSE");
   return 0;
}


답변

이 답변에서 지적했듯이 캡처 람다는 함수 포인터로 변환 할 수 없습니다 .

그러나 API 만 받아들이는 함수 포인터를 제공하는 것은 종종 고통스러운 일입니다. 가장 자주 인용되는 방법은 함수를 제공하고 정적 객체를 호출하는 것입니다.

static Callable callable;
static bool wrapper()
{
    return callable();
}

이것은 지루합니다. 우리는이 아이디어를 더욱 발전시키고 창조 과정을 자동화하고 wrapper삶을 훨씬 쉽게 만듭니다.

#include<type_traits>
#include<utility>

template<typename Callable>
union storage
{
    storage() {}
    std::decay_t<Callable> callable;
};

template<int, typename Callable, typename Ret, typename... Args>
auto fnptr_(Callable&& c, Ret (*)(Args...))
{
    static bool used = false;
    static storage<Callable> s;
    using type = decltype(s.callable);

    if(used)
        s.callable.~type();
    new (&s.callable) type(std::forward<Callable>(c));
    used = true;

    return [](Args... args) -> Ret {
        return Ret(s.callable(std::forward<Args>(args)...));
    };
}

template<typename Fn, int N = 0, typename Callable>
Fn* fnptr(Callable&& c)
{
    return fnptr_<N>(std::forward<Callable>(c), (Fn*)nullptr);
}

그리고 그것을 다음과 같이 사용하십시오

void foo(void (*fn)())
{
    fn();
}

int main()
{
    int i = 42;
    auto fn = fnptr<void()>([i]{std::cout << i;});
    foo(fn);  // compiles!
}

라이브

이것은 기본적으로 각 발생시 익명 함수를 선언하는 것입니다 fnptr.

호출하면 동일한 유형 fnptr의 이전에 작성된 callable주어진 호출 가능 항목 을 겹쳐 씁니다 . 우리는 이것을 어느 정도 int매개 변수로 해결 N합니다.

std::function<void()> func1, func2;
auto fn1 = fnptr<void(), 1>(func1);
auto fn2 = fnptr<void(), 2>(func2);  // different function


답변

C 함수 포인터로 람다를 사용하는 바로 가기는 다음과 같습니다.

"auto fun = +[](){}"

컬을 exmample로 사용하기 ( curl debug info )

auto callback = +[](CURL* handle, curl_infotype type, char* data, size_t size, void*){ //add code here :-) };
curl_easy_setopt(curlHande, CURLOPT_VERBOSE, 1L);
curl_easy_setopt(curlHande,CURLOPT_DEBUGFUNCTION,callback);


답변

템플릿 접근 방식은 여러 가지 이유로 영리하지만 람다의 수명주기와 캡처 된 변수를 기억하는 것이 중요합니다. 람다 포인터의 어떤 형태를 사용하고 람다는 하향이 아닌 경우, 복사 [=] 람다 만 사용해야합니다. 즉, 캡처 된 포인터의 수명 (스택 풀기)이 람다의 수명보다 짧은 경우 스택의 변수에 대한 포인터를 캡처하는 것은 안전하지 않습니다.

포인터로 람다를 캡처하는 간단한 솔루션은 다음과 같습니다.

auto pLamdba = new std::function<...fn-sig...>([=](...fn-sig...){...});

예를 들어 new std::function<void()>([=]() -> void {...}

delete pLamdba람다 메모리가 누출되지 않도록 나중에 기억하십시오 . 여기서 람다는 람다는 람다를 캡처 할 수 있으며 (작동 방식을 스스로 물어보십시오) std::function일반적으로 람다 구현에는 람다 (및 캡처 된) 데이터의 크기에 액세스 할 수있는 충분한 내부 정보가 포함되어야합니다 ( 그렇기 때문에 delete[캡쳐 된 유형의 소멸자를 실행]해야합니다.


답변

직접적인 대답은 아니지만 “functor”템플릿 패턴을 사용하여 람다 유형의 세부 사항을 숨기고 코드를 훌륭하고 간결하게 유지하는 약간의 변형입니다.

나는 당신이 결정 클래스를 어떻게 사용하고 싶었는지 확신하지 못했기 때문에 클래스를 사용하는 함수로 클래스를 확장해야했습니다. https://godbolt.org/z/jtByqE에서 전체 예를 참조하십시오.

수업의 기본 형태는 다음과 같습니다.

template <typename Functor>
class Decide
{
public:
    Decide(Functor dec) : _dec{dec} {}
private:
    Functor _dec;
};

다음과 같이 사용되는 클래스 유형의 일부로 함수 유형을 전달하는 경우 :

auto decide_fc = [](int x){ return x > 3; };
Decide<decltype(decide_fc)> greaterThanThree{decide_fc};

다시 말하지만, 왜 당신이 x람다에 전달하는 매개 변수를 갖는 것이 더 의미 가 있는지를 확신하지 못했기 때문에 다음과 같이 사용할 수 있습니다.

int result = _dec(5); // or whatever value

완전한 예를 보려면 링크를 참조하십시오