C ++에서 이동 의미론-로컬 변수의 이동 리턴


11

내 이해는 C ++ 11에서 값으로 함수에서 로컬 변수를 반환 할 때 컴파일러가 해당 변수를 r 값 참조로 취급하고 반환 할 함수에서 '이동'할 수 있다는 것입니다. 물론 RVO / NRVO는 발생하지 않습니다).

내 질문은 이것이 기존 코드를 깨뜨릴 수 없다는 것입니다.

다음 코드를 고려하십시오.

#include <iostream>
#include <string>

struct bar
{
  bar(const std::string& str) : _str(str) {}
  bar(const bar&) = delete;
  bar(bar&& other) : _str(std::move(other._str)) {other._str = "Stolen";}
  void print() {std::cout << _str << std::endl;}

  std::string _str;
};

struct foo
{
  foo(bar& b) : _b(b) {}
  ~foo() {_b.print();}

  bar& _b;
};

bar foobar()
{
  bar b("Hello, World!");
  foo f(b);

  return std::move(b);
}

int main()
{
  foobar();
  return EXIT_SUCCESS;
}

내 생각은 로컬 객체의 소멸자가 암시 적으로 이동 된 객체를 참조 할 수 있으므로 예기치 않게 '빈'객체를 볼 수 있다고 생각했습니다. 나는 이것을 테스트하려고했지만 ( http://ideone.com/ZURoeT 참조 ),에 명시 적 인없이 '정확한'결과를 얻었 std::move습니다 foobar(). 나는 그것이 NRVO 때문이라고 생각하지만, 그것을 비활성화하기 위해 코드를 재 배열하려고하지 않았습니다.

이 변환 (함수를 벗어나게 함)이 암시 적으로 발생하고 기존 코드를 손상시킬 수 있다는 것이 맞습니까?

업데이트 여기 내가 말하는 것을 보여주는 예가 있습니다. 다음 두 링크는 ​​동일한 코드에 대한 것입니다. http://ideone.com/4GFIRu-C ++ 03 http://ideone.com/FcL2Xj-C ++ 11

출력을 보면 다르게 보입니다.

따라서이 질문이 이제 표준으로 암시 적 이동을 추가 할 때 고려 되었습니까? 이런 종류의 코드가 드물기 때문에이 주요 변경 사항을 추가하는 것이 좋기로 결정되었습니다. 또한 이런 경우 컴파일러가 경고하는지 궁금합니다 ...


이동은 항상 개체를 파괴 가능한 상태로 두어야합니다.
Zan Lynx

예, 그러나 그것은 질문이 아닙니다. C ++ 11 이전 코드는 지역 변수의 값이 단순히 반환으로 인해 변경되지 않는다고 가정 할 수 있으므로 이러한 암시 적 이동은 해당 가정을 어길 수 있습니다.
Bwmat

그것이 나의 예에서 설명하려고하는 것입니다. 소멸자를 통해 return 문이 실행 된 후 함수가 실제로 반환되기 전에 함수의 로컬 변수 상태를 검사 할 수 있습니다.
Bwmat

이것은 당신이 추가 한 예제와 함께 훌륭한 질문입니다. 나는 이것이 이것을 설명 할 수있는 전문가들로부터 더 많은 답변을 얻길 바랍니다. 내가 줄 수있는 유일한 실제 피드백은 객체가 일반적으로 데이터에 대한 비 소유 뷰를 가져서는 안되는 이유입니다. 객체에 비 소유 뷰 (원시 포인터 또는 참조)를 제공 할 때 segfaults를 작성하는 순진한 코드를 작성하는 방법에는 여러 가지가 있습니다. 원하는 경우 적절한 답변으로 이에 대해 자세히 설명 할 수는 있지만 실제로 듣고 싶지 않은 것 같습니다. 그리고 btw는 11이 기존 키워드를 깨뜨릴 수 있다는 것을 이미 알고 있습니다.
Nir Friedman

예, C ++ 11은 오래된 코드를 깨뜨리지 않는다고 주장한 적이 있지만 이것은 매우 미묘하며 실제로 놓치기 쉽습니다 (컴파일러 오류, 경고, segfaults 없음)
Bwmat

답변:


8

Scott Meyers 암묵적인 이동 생성자가 C ++ 03 클래스 불변을 깰 수있는 문제에 대해 comp.lang.c ++ (2010 년 8 월)에 게시 했습니다.

struct X
{
  // invariant: v.size() == 5
  X() : v(5) {}

  ~X() { std::cout << v[0] << std::endl; }

private:    
  std::vector<int> v;
};

int main()
{
    std::vector<X> y;
    y.push_back(X()); // X() rvalue: copied in C++03, moved in C++0x
}

여기서 문제는 C ++ 03에서 멤버가 항상 5 개의 요소를 가지고 X있다는 불변성을 가지고 있다는 것 v입니다. X::~X()이 불변에 의존하지만 새로 도입 된 이동 생성자가에서 이동 v하여 길이를 0으로 설정했습니다.

깨진 불변 값이 X의 소멸자 에서만 감지되기 ​​때문에 예제와 관련이 있습니다 (로컬 객체의 소멸자가 암시 적으로 이동하는 객체를 참조하여 예기치 않게 객체를 볼 수 있음 ).

C ++ 11은 기존 코드 중 일부를 중단하고 이동 생성자를 기반으로 유용한 최적화를 제공하는 것 사이의 균형을 유지하려고합니다.

위원회는 처음에 이동 생성자와 이동 할당 연산자를 사용자가 제공하지 않으면 컴파일러에서 생성해야한다고 결정했습니다.

그런 다음 이것이 실제로 알람의 원인이라고 판단하고 기존 코드가 깨질 가능성이 적지 만 (예 : 명시 적으로 정의 된 소멸자) 이동 생성자 및 이동 할당 연산자의 자동 생성을 제한했습니다.

사용자 정의 소멸자가있을 때 암시 적 이동 생성자의 생성을 막는 것으로 충분하지만 사실이 아니라고 생각하고 있습니다 ( 자세한 내용은 N3153-암시 적 이동해야 함 ).

에서 N3174 - 이동하지 이동하려면 Stroupstrup는 말합니다 :

나는 단순한 역 호환성 문제가 아니라 언어 설계 문제라고 생각합니다. 오래된 코드를 깨는 것을 피하는 것은 쉽지만 (예를 들어 C ++ 0x에서 이동 작업 제거) 이동 작업을 널리 퍼뜨리는 C ++ 0x를 더 나은 언어로 만드는 것은 C + +98 코드

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.