전역 람다를 사용하지 않는 이유는 무엇입니까?


89

내부에 캡처되지 않은 람다를 사용하는 함수가 있습니다.

void foo() {
  auto bar = [](int a, int b){ return a + b; }

  // code using bar(x,y) a bunch of times
}

이제 람다에 의해 구현 된 기능이 다른 곳에서 필요해 졌으므로 람다를 foo()전역 / 네임 스페이스 범위로 들어 올릴 것 입니다. 람다로 남겨 두거나 복사하여 붙여 넣기 옵션으로 만들거나 적절한 기능으로 변경할 수 있습니다.

auto bar = [](int a, int b){ return a + b; } // option 1
int bar(int a, int b){ return a + b; } // option 2

void foo() {
  // code using bar(x,y) a bunch of times
}

적절한 기능으로 변경하는 것은 사소한 일이지만 람다로 남겨 두지 않는 이유가 있는지 궁금해 했습니다. "일반적인"전역 함수 대신 어디에서나 람다를 사용하지 않는 이유가 있습니까?


람다를주의해서 사용하지 않으면 의도하지 않은 변수를 캡처하면 많은 오류가 발생한다고 가정합니다.
macroland

답변:


61

전역 람다를 사용하지 않는 중요한 이유가 하나 있습니다. 그것이 정상적이지 않기 때문입니다.

C ++의 정규 함수 구문은 C 시절부터 사용되어 왔습니다. 프로그래머는 수십 년 동안 구문의 의미와 작동 방식을 알고 있습니다 (단, 함수 간 포인터 붕괴는 노련한 프로그래머에게도 영향을 미칩니다). "새로운 초보자"이상의 기술 수준의 C ++ 프로그래머가 함수 정의를 볼 경우, 그들이 무엇을 얻고 있는지 알고 있습니다.

세계적인 람다는 완전히 다른 짐승입니다. 일반 기능과는 다른 동작을합니다. 람다는 객체이지만 기능은 아닙니다. 유형이 있지만 해당 유형은 기능 유형과 다릅니다. 기타 등등.

이제 다른 프로그래머와 의사 소통하는 데있어 기준을 높였습니다. C ++ 프로그래머는이 함수의 기능을 이해하려면 람다를 이해해야합니다. 그리고 그렇습니다 .2019 년이므로 괜찮은 C ++ 프로그래머는 람다의 모양을 알아야합니다. 그러나 여전히 높은 수준입니다.

그리고 그들이 그것을 이해하더라도, 그 프로그래머의 마음에 대한 질문은 ...이 코드의 작성자가 왜 그렇게 작성 했습니까? 그리고 해당 질문에 대한 정답이없는 경우 (예 : 범위 사용자 지정 지점에서와 같이 명시 적으로 오버로드를 금지 하려는 경우) 공통 메커니즘을 사용해야합니다.

적절한 경우 새로운 솔루션보다 예상되는 솔루션을 선호하십시오. 가장 복잡한 방법으로 요점을 파악하십시오.


9
"C의 시대부터"
그전의

4
@LightnessRaces : 저의 주요 요점은 C ++의 함수 구문이 C 이후로 왔다는 것을 표현하는 것이 었습니다.
Nicol Bolas 2014

2
첫 번째 문장을 제외하고이 답변에 모두 동의하십시오. "정상적이지 않다"는 애매하고 성가신 진술이다. 아마도 당신은 "흔하지 않고 혼란 스럽다"는 의미에서 그것을 의미했을까요?
einpoklum

1
@einpoklum : C ++에는 도메인 내에서 여전히 "정상적인" "흔하지 않고 혼란스러운"것으로 간주 될 수있는 많은 것들이 있습니다 (딥 템플릿 메타 프로그래밍 참조). 이 경우 "정상"이 완벽하게 유효하다고 생각합니다. 역사적으로나 오늘날 C ++ 프로그래밍 경험의 "표준"내에있는 것은 아닙니다.
Nicol Bolas

@NicolBolas :하지만 그런 의미에서 순환 추론입니다. OP는 우리가 드문 방법을 채택해야하는지 궁금합니다. 희귀 관행은 거의 정의상 "표준"이 아닙니다. 그러나 nm.
einpoklum

53

전역 함수를 일반 함수의 대체 대체물로 사용하지 않으려는 몇 가지 이유를 생각할 수 있습니다.

  • 일반 기능이 과부하 될 수 있습니다. 람다 수 없습니다 (그러나 이것을 시뮬레이션하는 기술이 있습니다)
  • 그것들이 함수와 유사하다는 사실에도 불구하고, 이와 같이 캡처하지 않는 람다조차도 메모리를 차지합니다 (일반적으로 캡처하지 않는 경우 1 바이트).
    • 주석에서 지적했듯이 최신 컴파일러는 as-if 규칙에 따라이 저장소를 최적화합니다.

"왜 스테이트 풀 펑터 (클래스)를 교체하기 위해 람다를 사용해서는 안됩니까?"

  • 클래스는 단순히 람다보다 제한이 적으므로 가장 먼저 도달해야합니다.
    • (공개 / 개인 데이터, 과부하, 헬퍼 방법 등)
  • 람다에 상태가 있다면 그것이 언제 전역이되는지에 대해 추론하기가 더 어렵다.
    • 가장 좁은 범위에서 클래스 의 인스턴스 를 만드는 것이 좋습니다
  • 캡처되지 않는 람다를 함수 포인터로 변환하는 것은 이미 어렵고 캡처에 무언가를 지정하는 람다는 불가능합니다.
    • 클래스는 함수 포인터를 만드는 간단한 방법을 제공하며 많은 프로그래머가 더 편하게 사용할 수있는 방법이기도합니다.
  • 캡처가있는 Lambdas는 기본 생성이 불가능합니다 (C ++ 20에서는 이전에는 기본 생성자가 없었습니다)

람다를 호출 할 때 함수를 호출 할 때 추가 매개 변수를 생성하는 람다에 대한 암시 적 "this"포인터도 있습니다 (캡처하지 않는 것도 포함).
1201ProgramAlarm

@ 1201ProgramAlarm : 좋은 지적입니다. 람다에 대한 함수 포인터를 얻는 것이 훨씬 더 어려울 수 있습니다 (비 캡처 람다는 일반 함수 포인터로 쇠퇴 할 수 있음)
AndyG

3
반대로, 직접 호출하는 대신 어딘가에 전달되는 경우 함수 대신 람다를 사용하면 인라인이 촉진됩니다. 물론, 하나는에서 함수 포인터를 팩 수 std::integral_constant... 그것에 대해
Deduplicator

@Deduplicator : " 함수 대신 람다를 사용하면 인라이닝이 촉진 됩니다." "어딘가"가 함수 포인터가 아닌 임의의 호출 가능한 유형으로 호출하는 함수를 취하는 템플릿 인 경우에만 적용됩니다.
Nicol Bolas

2
@AndyG 당신은 as-if 규칙을 잊고 있습니다 : 람다의 크기를 요구하지 않는 프로그램은 (왜… !?) 포인터를 만들거나 필요하지 않습니다 (일반적으로 필요하지 않습니다) 공간을 할당하십시오.
Konrad Rudolph

9

묻고 나서 나는 이것을하지 않는 이유를 생각했습니다. 이것은 변수이기 때문에 정적 초기화 순서 Fiasco ( https://isocpp.org/wiki/faq/ctors#static-init-order )에 취약합니다. 줄 아래로 버그가 발생합니다.


당신 그것들을 constexpr람다로 만들 수 있습니다 ... 실제 요점은 : 그냥 함수를 사용하십시오.
einpoklum

8

"일반적인"전역 함수 대신 어디서나 람다를 사용하지 않는 이유가 있습니까?

특정 수준의 복잡성의 문제에는 최소한 동일한 복잡성의 솔루션이 필요합니다. 그러나 동일한 문제에 대해 덜 복잡한 해결책이 있다면 더 복잡한 것을 사용하는 것에 대한 정당성이 없습니다. 왜 복잡성이 필요하지 않습니까?

람다와 함수 사이에서 함수는 단순히 덜 복잡한 종류의 엔티티입니다. 람다를 사용하지 않는 것을 정당화 할 필요는 없습니다. 당신은 하나를 사용하여 정당화해야합니다. 람다 식은 클로저 형식을 소개합니다. 클로저 형식은 일반적인 모든 특수 멤버 함수, 함수 호출 연산자 및이 경우 함수 포인터에 대한 암시 적 변환 연산자를 가진 명명되지 않은 클래스 형식이며 해당 형식의 개체를 만듭니다. 복사 - 초기화 람다 표현식에서 전역 변수는 단순히 않습니다 훨씬 더 단순한 기능을 정의하는 것보다. 6 개의 암시 적으로 선언 된 함수로 클래스 유형을 정의하고 두 개의 추가 연산자 함수를 정의하며 오브젝트를 작성합니다. 컴파일러는 더 많은 일을해야합니다. 람다의 기능이 필요하지 않으면 람다를 사용하지 마십시오.


6

람다는 익명 함수 입니다.

명명 된 람다를 사용하는 경우 기본적으로 명명 된 익명 함수를 사용하고 있음을 의미합니다. 이 옥시 모론을 피하기 위해 함수를 사용할 수도 있습니다.


1
이것은 용어의 논쟁으로 보인다. 혼란스러운 이름과 의미. 명명 된 람다는 더 이상 익명 (duh)이 아님을 정의하여 쉽게 반박 할 수 있습니다. 여기서 문제는 람다가 사용되는 방식이 아니라 "익명 함수"가 잘못된 것이며 람다가 이름에 바인딩 될 때 더 이상 정확하지 않다는 것입니다.
Konrad Rudolph

@ KonradRudolph : 그것은 용어가 아니기 때문에 처음부터 만들어졌습니다. 적어도 역사적으로 람다는 익명의 함수이므로 특히 함수가 예상되는 경우 이름을 지정하는 것이 혼란 스럽습니다.
Eric Duminil '12

1
아냐 람다는 일상적으로 (일반적으로 로컬로) 명명되며, 그것에 대해 혼란스러운 것은 없습니다.
Konrad Rudolph

1
익명 함수에 동의 하지 않으면 더 재미있는 기능 이지만 어쨌든 rvalue-reference로만 사용하도록 강제하는 대신 (이름이 지정된) 인스턴스가있는 문제는 보이지 않습니다. (귀하의 요지가 현지 범위에서도 적용되어야 함).
Jarod42

5

람다로 두지 말아야 할 이유가 있다면? "일반적인"전역 함수 대신 어디서나 람다를 사용하지 않는 이유가 있습니까?

우리는 전역 functor 대신 함수를 사용 했으므로 놀랍게도 일관성과 원리를 깨뜨 렸습니다 .

주요 차이점은 다음과 같습니다.

  • 함수는 오버로드 될 수 있지만 펑 터는 그렇지 않습니다.
  • 함수는 functors가 아닌 ADL로 찾을 수 있습니다.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.