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.