람다 자체에서 C ++ 람다 함수의 주소를 얻는 방법은 무엇입니까?


53

람다 함수의 주소를 얻는 방법을 알아 내려고 노력 중입니다. 다음은 샘플 코드입니다.

[]() {
    std::cout << "Address of this lambda function is => " << ????
}();

변수에서 람다를 캡처하고 주소를 인쇄 할 수 있다는 것을 알고 있지만이 익명 함수가 실행될 때 수행하고 싶습니다.

더 간단한 방법이 있습니까?


24
이것은 호기심을위한 것일까 요, 아니면 해결해야 할 근본적인 문제가 있습니까? 근본적인 문제가 있다면, (우리를 위해) 알려지지 않은 문제에 대한 하나의 가능한 해결책을 요구하는 대신 직접 질문하십시오.
일부 프로그래머 친구

41
... XY 문제를 효과적으로 확인합니다.
ildjarn

8
람다를 수동으로 작성된 functor 클래스로 바꾼 다음을 사용할 수 있습니다 this.
HolyBlackCat

28
"자체 내에서 람 바어 함수의 주소를 얻기"는 것이다 솔루션 , 당신이 좁게 집중 해결책은. 더 나은 다른 솔루션이있을 수 있습니다. 그러나 실제 문제가 무엇인지 알 수 없으므로 도움을 드릴 수 없습니다. 우리는 당신이 주소를 어떻게 사용할 것인지조차 모릅니다. 내가하려는 것은 실제 문제를 해결하는 것입니다.
일부 프로그래머 친구

8
@Someprogrammerdude 당신이 말하는 대부분의 내용이 합리적이지만 " X는 어떻게 할 수 있습니까?"라는 질문에는 문제가 없습니다 . 여기서 X 는 "내부에서 람다의 주소를 얻는 것"입니다. 주소가 어떤 용도로 사용되는지 알 수 없으며 중요하지 않은 코드 기반에서 실행 가능하거나 불가능할 수있는 다른 사람의 의견으로는 "더 나은"솔루션이있을 수도 있습니다 ( 우리에게). 더 나은 아이디어는 명시된 문제에 집중하는 것입니다. 이것은 가능하거나 그렇지 않습니다. 그렇다면 어떻게 ? 그렇지 않다면 언급하지 않았으며 다른 제안이있을 수 있습니다 (IMHO).
code_dredd

답변:


32

직접 가능하지 않습니다.

그러나 람다 캡처는 클래스이며 객체의 주소는 첫 번째 멤버의 주소와 일치합니다. 따라서 첫 번째 캡처로 하나의 개체를 값으로 캡처하면 첫 번째 캡처의 주소는 람다 개체의 주소와 일치합니다.

int main() {
    int i = 0;
    auto f = [i]() { printf("%p\n", &i); };
    f();
    printf("%p\n", &f);
}

출력 :

0x7ffe8b80d820
0x7ffe8b80d820

또는 람다 캡처에 대한 참조를 호출 연산자로 전달 하는 데코레이터 디자인 패턴 람다를 만들 수 있습니다 .

template<class F>
auto decorate(F f) {
    return [f](auto&&... args) mutable {
        f(f, std::forward<decltype(args)>(args)...);
    };
}

int main() {
    auto f = decorate([](auto& that) { printf("%p\n", &that); });
    f();
}

15
"오브젝트의 주소가 첫 번째 멤버의 주소와 일치합니다"캡처가 주문되거나 보이지 않는 멤버가없는 위치에 지정되어 있습니까?
n. '대명사'm.

35
@n. '대명사입니다. 아니, 이것은 이식 불가능한 솔루션입니다. 캡처 구현은 패딩을 최소화하기 위해 멤버를 최대에서 최소로 정렬 할 수 있습니다. 표준은 명시 적으로 허용합니다.
Maxim Egorushkin

14
다시, "이것은 휴대용 솔루션이 아닙니다." 그것은 정의되지 않은 행동
Solomon Slow

1
@ruohola 말하기 어렵다. "객체의 주소는 첫 번째 구성원의 주소와 일치합니다"는 표준 레이아웃 유형에 해당됩니다. UB를 호출하지 않고 람다 유형이 표준 레이아웃인지 테스트 한 경우 UB를 발생시키지 않고이를 수행 할 수 있습니다. 결과 코드는 구현에 따라 동작합니다. 그러나 합법성을 먼저 테스트하지 않고 트릭을 수행하는 것은 UB입니다.
벤 Voigt

4
§ 8.1.5.2, 15에 따라 지정되지 않았다고 생각합니다 . 람다 표현이 평가 될 때 복사로 캡처 된 엔티티는 결과 클로저 객체의 각 해당 비 정적 데이터 멤버를 직접 초기화하는 데 사용됩니다. 초기화 캡쳐에 대응하는 비 정적 데이터 멤버는 대응하는 초기화기에 의해 지시 된 바와 같이 초기화된다 (...). (배열 멤버의 경우 배열 요소는 아래 첨자 순서대로 직접 초기화됩니다.) 이러한 초기화는 비 정적 데이터 멤버가 선언 된 ( 지정되지 않은 ) 순서로 수행됩니다 .
Erbureth는 Reinstate Monica가

51

람다 내에서 람다 객체의 주소를 직접 가져올 수있는 방법이 없습니다.

이제 이런 일이 발생하면 매우 유용합니다. 가장 일반적으로 사용되는 것은 재귀입니다.

y_combinator정의 된 곳 당신이 때까지 자신에 대해 말할 수없는 언어에서 비롯됩니다. 에서 매우 쉽게 구현할 수 있습니다 .

template<class F>
struct y_combinator {
  F f;
  template<class...Args>
  decltype(auto) operator()(Args&&...args) const {
    return f( f, std::forward<Args>(args)... );
  }
  template<class...Args>
  decltype(auto) operator()(Args&&...args) {
    return f( f, std::forward<Args>(args)... );
  }
};

이제 당신은 이것을 할 수 있습니다 :

y_combinator{ [](auto& self) {
  std::cout<<"Address of this lambda function is => "<< &self;
} }();

이에 대한 변형은 다음과 같습니다.

template<class F>
struct y_combinator {
  F f;
  template<class...Args>
  decltype(auto) operator()(Args&&...args) const {
    return f( *this, std::forward<Args>(args)... );
  }
  template<class...Args>
  decltype(auto) operator()(Args&&...args) {
    return f( *this, std::forward<Args>(args)... );
  }
};

여기서 self전달 된 self인수는 첫 번째 인수로 전달되지 않고 호출 될 수 있습니다 .

두 번째는 실제 y 콤비 네이터 (일명 고정 소수점 콤비 네이터)와 일치합니다. 당신이 원하는 것은 '람다의 주소'가 무엇을 의미하는지에 달려 있습니다.


3
와우, Y 콤비 네이터는 Lisp / Javascript / Python과 같은 동적 유형 언어로 머리를 감쌀 수있을만큼 단단합니다. 나는 C ++에서 하나를 볼 것이라고 생각하지 못했습니다.
Jason S

13
C ++에서이 작업을 수행하면 체포 될 자격이 있습니다.
user541686

3
@MSalters Uncertain. F표준 레이아웃이 아닌 경우 표준 레이아웃 y_combinator이 아니므로 적절한 보증이 제공되지 않습니다.
Yakk-Adam Nevraumont

2
@carto 상위 답변은 람다가 범위 내에 있고 유형 삭제 오버 헤드를 신경 쓰지 않는 경우에만 작동합니다. 세 번째 답변은 y 조합입니다. 두 번째 답변은 수동 ycombinator입니다.
야크-아담 네 브라우 몬트

2
@kaz C ++ 17 기능. 11/14에서는 F를 deduxe 할 make 함수를 작성할 것입니다. 17에서 템플릿 이름 (때로는 추론 가이드)으로 추론 할 수 있습니다
Yakk-Adam Nevraumont

25

이 문제를 해결하는 한 가지 방법은 람다를 손으로 쓴 functor 클래스로 바꾸는 것입니다. 또한 람다는 본질적으로 후드 아래에 있습니다.

그러면 thisfunctor를 변수에 할당하지 않아도 주소를 통해 주소를 얻을 수 있습니다 .

#include <iostream>

class Functor
{
public:
    void operator()() {
        std::cout << "Address of this functor is => " << this;
    }
};

int main()
{
    Functor()();
    return 0;
}

산출:

Address of this functor is => 0x7ffd4cd3a4df

이것은 이것이 100 % 휴대 가능하고 추론하고 이해하기가 매우 쉽다는 장점이 있습니다.


9
펑 터는 람다처럼 선언 될 수도 있습니다 :struct { void operator()() { std::cout << "Address of this functor is => " << this << '\n'; } } f;
오류

-1

람다를 포착하십시오.

std::function<void ()> fn = [&fn]() {
  std::cout << "My lambda is " << &fn << std::endl;
}

1
여기서 유연성은 std::function필요하지 않으며 상당한 비용이 듭니다. 또한 해당 객체를 복사 / 이동하면 객체가 손상됩니다.
중복 제거기

@Deduplicator 왜 이것이 표준을 준수하는 유일한 답변이기 때문에 왜 필요하지 않습니까? 작동하고 std :: function이 필요하지 않은 답변을 제공하십시오.
Vincent Fourmond

유일한 요점은 람다의 주소를 얻는 것이 아닌 한 (더 그 자체로는 이해가되지 않는) 더 좋고 명확한 해결책 인 것 같습니다. 일반적인 사용 사례는 재귀 목적 예를 참조하십시오, 자체 내부하여 lambla에 액세스하는 것입니다 : stackoverflow.com/questions/2067988/... 함수로 선언 옵션을 널리 :) 솔루션으로 받아 들여졌다
애비

-6

가능하지만 플랫폼 및 컴파일러 최적화에 크게 의존합니다.

내가 아는 대부분의 아키텍처에는 명령 포인터라는 레지스터가 있습니다. 이 솔루션의 핵심은 함수 내부에있을 때 추출하는 것입니다.

amd64에서 다음 코드는 함수 1에 가까운 주소를 제공해야합니다.

#include <iostream>

void* foo() {
    void* n;
    asm volatile("lea 0(%%rip), %%rax"
      : "=a" (n));
    return n;
}

auto boo = [](){
    void* n;
    asm volatile("lea 0(%%rip), %%rax"
       : "=a" (n));
    return n;
};

int main() {
    std::cout<<"foo"<<'\n'<<((void*)&foo)<<'\n'<<foo()<<std::endl;  
    std::cout<<"boo"<<'\n'<<((void*)&boo)<<'\n'<<boo()<<std::endl;
}

그러나 GCC에 예를 들어 https://godbolt.org/z/dQXmHm-O3최적화 수준의 기능 인라인 될 수 있습니다.


2
나는 공감하고 싶지만, 나는 그다지 활발하지 않으며 여기서 일어나는 일을 이해하지 못한다. 작동 원리에 대한 설명은 정말 귀중 할 것입니다. 또한 " 기능에 가까운 주소"는 무엇을 의미 합니까? 상수 / 정의되지 않은 오프셋이 있습니까?
R2RT

2
@majkrzak 이것은 게시 된 모든 것 중에서 가장 휴대하기 쉽기 때문에 "진정한"답변이 아닙니다. 람다 자체의 주소를 반환한다고 보장되지는 않습니다.
anonymous

그것은 "그렇지만 불가능하다"라는 대답은 틀린 답이다
majkrzak

명령어 포인터를 사용하여 자동 또는 thread_local저장 시간 이있는 객체의 주소를 파생시킬 수 없습니다 . 여기서 얻으려는 것은 객체가 아니라 함수의 반환 주소입니다. 그러나 컴파일러 생성 함수 프롤로그가 스택으로 푸시하고 로컬 변수를위한 공간을 만들기 위해 스택 포인터를 조정하기 때문에 작동하지 않습니다.
Maxim Egorushkin 21시 51 분
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.