const 참조로 람다 캡처?


166

람다 식에서 const 참조로 캡처 할 수 있습니까?

예를 들어 아래 표시된 과제가 실패하고 싶습니다.

#include <cstdlib>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;

int main()
{
    string strings[] = 
    {
        "hello",
        "world"
    };
    static const size_t num_strings = sizeof(strings)/sizeof(strings[0]);

    string best_string = "foo";

    for_each( &strings[0], &strings[num_strings], [&best_string](const string& s)
      {
        best_string = s; // this should fail
      }
    );
    return 0;
}

업데이트 : 이것은 오래된 질문이므로 C ++ 14에 도움이되는 기능이 있으면 업데이트하는 것이 좋습니다. C ++ 14의 확장 기능을 사용하면 const 참조로 비 const 객체를 캡처 할 수 있습니까? ( 2015 년 8 월 )


람다는 다음과 같이 보이지 않아야합니다 [&, &best_string](string const s) { ...}.
erjot

3
일관되지 않은 캡처. "const &"는 람다 함수에서 액세스해야하지만 수정되지 않아야하는 큰 const 객체가있을 때 매우 유용 할 수 있습니다.
sergtk

코드를보고. 두 개의 매개 변수 lambda를 사용하고 두 번째 매개 변수를 const 참조로 바인딩 할 수 있습니다. 그래도 비용이 함께 제공됩니다.
Alex

1
이것은 C ++ 11에서는 불가능합니다. 그러나 아마도 우리는 C ++ 14에 대해이 질문을 업데이트 할 수 있습니다-이것을 허용하는 확장이 있습니까? C ++ 14 일반화 된 람다 캡처?
Aaron McDaid

답변:


127

const n3092 기준으로 캡처 문법에 없습니다.

capture:
  identifier
  & identifier
  this

이 텍스트는 사본 별 캡처 및 기준 별 캡처 만 언급하고 어떤 종류의 constness도 언급하지 않습니다.

나는 감독에 대한 느낌이지만, 표준화 과정을 매우 밀접하게 따르지 않았습니다.


47
방금 변경 가능한 캡처에서 수정 된 변수의 버그를 추적했지만 const. 더 정확하게는 캡처 변수가 const인 경우 컴파일러는 프로그래머에게 올바른 동작을 강제했을 것입니다. 구문이 지원되면 좋을 것 [&mutableVar, const &constVar]입니다.
Sean

C ++ 14에서 가능 해야하는 것처럼 보이지만 작동하지 않습니다. 어떤 제안?
Aaron McDaid

37
일관성은 캡처 된 변수에서 상속됩니다. 당신은 캡처 할 그래서 경우 aconst, 선언 const auto &b = a;람다 캡처 전b
StenSoft

7
@StenSoft Bleargh. 참조로 멤버 변수를 캡처 할 때 분명히 적용되지 않습니다 [&foo = this->foo]. const함수 내부 에서 캡처 자체가 한정자를 삭제 한다는 오류가 발생합니다 . 그러나 이것은 GCC 5.1의 버그 일 수 있다고 생각합니다.
Kyle Strand

119

사용 static_cast/ const_cast:

[&best_string = static_cast<const std::string&>(best_string)](const string& s)
{
    best_string = s; // fails
};

데모


사용하여 std::as_const:

[&best_string = std::as_const(best_string)](const string& s)
{
    best_string = s; // fails
};

데모 2


또한 아마도 이것이 허용되는 답변으로 편집되어야합니까? 어느 쪽이든, c ++ 11과 c ++ 14를 모두 다루는 좋은 대답이 있어야합니다. 비록 c ++ 14가 앞으로 몇 년 동안 모두에게 충분할 것이라고 주장 할 수 있습니다.
Aaron McDaid

12
@AaronMcDaid const_cast는 무조건 휘발성 객체를 const 객체로 캐스팅 할 const수 있으므로 ( 캐스트 요청시 ) 제약 조건을 추가하기 위해 선호합니다.static_cast
Piotr Skotnicki

1
@PiotrSkotnicki는 다른 한편으로는, static_cast정확히 오른쪽 유형을하지 않은 경우 const를 기준으로 자동으로 임시을 만들 수 있습니다
MM

24
@MM &basic_string = std::as_const(best_string)은 모든 문제를 해결해야합니다
Piotr Skotnicki

14
즉, 쓰기 뭔가 끔찍한 방법되는 문제 제외하고 말입니다 @PiotrSkotnicki 한다 단순하게 수를 const& best_string.
Kyle Strand

12

const캡처 수단으로 캡처 부분을 지정해서는 안된다고 생각합니다 . 외부 범위 변수에 액세스하는 방법 만 있으면됩니다.

지정자는 외부 범위에서 더 잘 지정됩니다.

const string better_string = "XXX";
[&better_string](string s) {
    better_string = s;    // error: read-only area.
}

람다 함수 는 const (범위에서 값을 변경할 수 없음)이므로 값으로 변수를 캡처하면 변수를 변경할 수 없지만 참조는 람다 범위에 없습니다.


1
@ Amarnath Balasubramani : 그것은 단지 내 의견입니다. 람다 캡처 부분에 const 참조를 지정할 필요가 없다고 생각합니다. 왜 변수 const가 여기 있고 다른 곳에 const가 아닌지 (가능한 경우 오류가 발생하기 쉽습니다) ). 어쨌든 귀하의 답변을 드리겠습니다.
zhb

2
better_string포함 범위 내에서 수정해야하는 경우이 솔루션이 작동하지 않습니다. const-ref로 캡처하는 사용 사례는 포함 범위에서 변수를 변경할 수 있지만 람다 내에서 변수를 변경할 수없는 경우입니다.
Jonathan Sharman

@JonathanSharman, 변수에 대한 const 참조를 만드는 데 비용이 들지 않으므로 변수를 만들고 const string &c_better_string = better_string;람다에 전달할 수 있습니다 .[&c_better_string]
Steed

@Steed 그 문제는 추가 변수 이름을 주변 범위에 도입한다는 것입니다. 가변 범위를 최소화하면서 const-correctness를 달성하기 때문에 위의 Piotr Skotnicki 솔루션이 가장 깨끗하다고 ​​생각합니다.
Jonathan Sharman

@JonathanSharman, 여기서 우리는 의견의 땅에 들어갑니다. 내 요점은 두 솔루션 모두 작업에 적합하다는 것입니다.
Steed November

8

변수를 functor의 매개 변수로 사용하지 않는 경우 현재 함수의 액세스 수준을 사용해야합니다. 해서는 안된다고 생각되면이 함수와 람다를 분리하십시오.

어쨌든 다른 const 참조를 대신 사용하여 원하는 것을 쉽게 얻을 수 있습니다.

#include <cstdlib>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;

int main()
{
    string strings[] = 
    {
        "hello",
        "world"
    };
    static const size_t num_strings = sizeof(strings)/sizeof(strings[0]);

    string best_string = "foo";
    const string& string_processed = best_string;

    for_each( &strings[0], &strings[num_strings], [&string_processed]  (const string& s)  -> void 
    {
        string_processed = s;    // this should fail
    }
    );
    return 0;
}

그러나 이것은 람다가 현재 기능과 분리되어 람다가 아닌 것으로 가정하는 것과 같습니다.


1
캡처 절은 여전히 ​​언급 best_string합니다. 그 외에도 GCC 4.5는 의도 된 코드를 "성공적으로 거부"합니다.
sellibitze

예, 기술적으로 달성하려는 결과를 얻을 수 있습니다. 그러나 궁극적으로 내 원래 질문에 대한 대답은 "아니오"인 것 같습니다.
John Dibling

왜 이것이 "람다 (lambda)"가 아닌가?

람다의 본질은 상황에 따라 다르다는 것입니다. 특정 상황이 필요하지 않으면 펑터를 만드는 빠른 방법 일뿐입니다. functor가 컨텍스트 독립적 인 경우 실제 functor로 만드십시오.
클라 임

3
"functor가 문맥에 독립적이어야한다면, 실제 functor로 만드십시오"... 작별 인사를 할 수 있습니까?
앤드류 나사로

5

세 가지 옵션이 있다고 생각합니다.

  • const 참조를 사용하지 않고 사본 캡처를 사용하십시오.
  • 그것이 수정 가능하다는 사실을 무시하십시오
  • std :: bind를 사용하여 const 참조가있는 이진 함수의 인수 하나를 바인딩하십시오.

사본 사용

복사 캡처가있는 람다에 대한 흥미로운 부분은 실제로 읽기 전용이므로 원하는대로 정확하게 수행한다는 것입니다.

int main() {
  int a = 5;
  [a](){ a = 7; }(); // Compiler error!
}

std :: bind 사용

std::bind함수의 arity를 ​​줄입니다. 그러나 이로 인해 함수 포인터를 통해 간접 함수 호출이 발생할 수 있습니다.

int main() {
  int a = 5;
  std::function<int ()> f2 = std::bind( [](const int &a){return a;}, a);
}

1
포함 범위의 변수에 대한 변경을 제외하고는 람다에 반영되지 않습니다. 그것은 참조가 아니며, 재 할당이 의미하는 것처럼 보이지 않기 때문에 재 할당해서는 안되는 변수 일뿐입니다.
Grault


0

clang을 사용하거나이 gcc 버그가 수정 될 때까지 기다리십시오. 버그 70385 : const 참조를 참조하여 Lambda를 캡처하지 못했습니다 [ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70385 ]


1
이 링크가 질문에 대한 답변을 제공 할 수 있지만 여기에 답변의 필수 부분을 포함시키고 참조 용 링크를 제공하는 것이 좋습니다. 링크 된 페이지가 변경되면 링크 전용 답변이 유효하지 않게 될 수 있습니다.”
Div

좋아, 여기에 gcc 버그 설명을 추가하기 위해 답변을 편집했습니다.
user1448926

이것은 질문에 대한 간접적 인 대답입니다. 버그는 const를 캡처 할 때 컴파일러가 실패하는 방법에 관한 것이므로 아마도 문제의 문제를 해결하거나 해결할 수있는 방법이 gcc에서 작동하지 않을 수 있습니다.
Stein

0

const를 사용하면 단순히 알고리즘 앰 퍼가 있고 문자열을 원래 값으로 설정합니다. 즉, 람다는 실제로 함수의 매개 변수로 정의하지 않지만 주변 범위에는 추가 변수가 있습니다 ... 그러나 문자열을 일반적인 [&, & best_string] (string const s) 로 정의하지는 않습니다. 따라서 참조를 캡처하려고 시도하면 그대로 두는 것이 좋습니다.


매우 오래된 질문 : 귀하의 답변에 귀하가 참조하는 C ++ 버전과 관련된 컨텍스트가 부족합니다. 이 내용을 제공해주세요.
ZF007
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.