벡터가 커질 때 이동 의미론을 적용하는 방법은 무엇입니까?


93

나는이 std::vector특정 클래스의 객체를 A. 클래스는 사소하지 않으며 복사 생성자 이동 생성자가 정의되어 있습니다.

std::vector<A>  myvec;

A객체로 벡터를 채우면 (예 :) 벡터의 요소의 새 사본을 인스턴스화 myvec.push_back(a)하는 복사 생성자 A( const A&)를 사용하여 벡터의 크기가 커집니다 .

클래스의 이동 생성자가 A대신 사용 되도록 어떻게 든 강제 할 수 있습니까 ?


5
이동 인식 벡터 구현을 사용하여 할 수 있습니다.
K-ballo 2011

2
이것을 달성하는 방법을 좀 더 구체적으로 말씀해 주시겠습니까?
Bertwim van Beest 2011

1
이동 인식 벡터 구현을 사용하기 만하면됩니다. 표준 라이브러리 구현 (btw입니까?)이 이동을 인식하지 않는 것처럼 들립니다. Boost의 이동 인식 컨테이너를 사용해 볼 수 있습니다.
K-ballo 2011

1
글쎄, 나는 움직임을 인식하는 gcc 4.5.1을 사용합니다.
Bertwim van Beest 2011

내 코드에서는 이동 생성자에 명시 적 "noexcept"가 없더라도 복사 생성자를 개인용으로 만들었습니다.
Arne

답변:


129

std::vector를 사용하여 이동 생성자와 소멸자가 throw하지 않는다는 것을 C ++ (특히 ) 에 알려야 합니다 noexcept. 그런 다음 벡터가 커지면 이동 생성자가 호출됩니다.

다음에 의해 존중되는 이동 생성자를 선언하고 구현하는 방법입니다 std::vector.

A(A && rhs) noexcept { 
  std::cout << "i am the move constr" <<std::endl;
  ... some code doing the move ...  
  m_value=std::move(rhs.m_value) ; // etc...
}

생성자가 아닌 경우 noexcept, std::vector다음 표준이 요구하는 예외 보증을 보장 할 수 없기 때문에, 그것을 사용할 수 없습니다.

표준에서 말한 내용에 대한 자세한 내용은 C ++ Move 의미 체계 및 예외 를 참조하세요.

예외와 관련이있을 수 있다고 암시 한 Bo에게 감사합니다. 또한 Kerrek SB의 조언을 고려하고 emplace_back가능하면 사용 하십시오. 그것은 수 있습니다 빠른 (하지만 종종 아니다), 그것은 명확하고 컴팩트 수 있습니다,하지만 (특히 비 명시 적으로 생성자와) 몇 가지 함정도 있습니다.

편집 , 종종 기본값은 원하는 것입니다. 이동할 수있는 모든 항목을 이동하고 나머지는 복사합니다. 명시 적으로 요청하려면 다음을 작성하십시오.

A(A && rhs) = default;

그렇게하면 가능한 경우 noexcept가 발생합니다. 기본 Move 생성자가 noexcept로 정의되어 있습니까?

이전 버전의 Visual Studio 2015 및 이전 버전에서는 이동 의미 체계를 지원하더라도이를 지원하지 않았습니다.


관심의 부족, 어떻게 않습니다 IMPL은 있는지 "알" value_type의 이동의 ctor는 noexcept? 아마도 언어는 호출 범위가 noexcept함수일 때 함수 호출 후보 집합을 제한 합니까?
밝기 경주 궤도에

1
@LightnessRacesinOrbit 나는 그것이 en.cppreference.com/w/cpp/types/is_move_constructible 과 같은 일을하고 있다고 가정합니다 . 이동 생성자는 하나만있을 수 있으므로 선언에 의해 명확하게 정의되어야합니다.
Johan Lundberg

@LightnessRacesinOrbit, 이후 noexcept이동 생성자 가 있는지 실제로 알 수있는 (표준 / 유용한) 방법이 없다는 것을 배웠습니다 . 복사 생성자 is_nothrow_move_constructible가 있으면 참이됩니다 nothrow. 나는 값 비싼 nothrow복사 생성자 의 실제 사례를 알지 못하기 때문에 그것이 정말로 중요한 것인지 명확하지 않습니다.
요한 룬드 버그

나를 위해 작동하지 않습니다. 내 소멸자, 이동 생성자 및 이동 할당 함수는 noexcept모두 헤더와 구현에 모두 표시 되어 있으며 push_back (std :; move)를 수행하면 여전히 복사 생성자를 호출합니다. 여기서 머리카락을 찢고 있어요.
AlastairG 19

1
@Johan 문제를 발견했습니다. 나는 std::move()잘못된 push_back()전화를 사용 하고 있었다 . 눈앞에서 명백한 오류가 보이지 않는 문제를 너무 열심히 찾고있는 경우 중 하나입니다. 그리고 점심 시간이되어 댓글 삭제를 잊었습니다.
AlastairG 19

17

흥미롭게도 gcc 4.7.2의 벡터는 이동 생성자와 소멸자가 모두 인 경우에만 이동 생성자를 사용합니다 noexcept. 간단한 예 :

struct foo {
    foo() {}
    foo( const foo & ) noexcept { std::cout << "copy\n"; }
    foo( foo && ) noexcept { std::cout << "move\n"; }
    ~foo() noexcept {}
};

int main() {
    std::vector< foo > v;
    for ( int i = 0; i < 3; ++i ) v.emplace_back();
}

이것은 예상 된 결과를 출력합니다.

move
move
move

내가 제거 할 때, noexcept에서 ~foo(), 결과는 다릅니다 :

copy
copy
copy

이것도이 질문에 대한 답이라고 생각합니다 .


다른 답변은 이동 생성자에 대해서만 이야기하고 소멸자 가 noexcept 일 필요가 없다는 것 같습니다.
Nikola Benes 2013 년

글쎄, 그래야하지만, gcc 4.7.2에서는 그렇지 않았습니다. 그래서이 문제는 사실 gcc에만 국한되었습니다. 그래도 gcc 4.8.0에서 수정되어야합니다. 관련 stackoverflow 질문을 참조하십시오 .
Nikola Benes 2013 년

-1

std::vector재 할당시 이동 의미론을 사용 하도록 강제하는 유일한 방법 (C ++ 17 및 초기) 은 복사 생성자를 삭제하는 것 같습니다. :). 이런 식으로 이동 생성자를 사용하거나 컴파일 타임에 죽을 것입니다 :).

std::vector재 할당시 이동 생성자를 사용해서는 안되는 규칙이 많이 있지만 반드시 사용해야 하는 위치에 대해서는 없습니다 .

template<class T>
class move_only : public T{
public:
   move_only(){}
   move_only(const move_only&) = delete;
   move_only(move_only&&) noexcept {};
   ~move_only() noexcept {};

   using T::T;   
};

라이브

또는

template<class T>
struct move_only{
   T value;

   template<class Arg, class ...Args, typename = std::enable_if_t<
            !std::is_same_v<move_only<T>&&, Arg >
            && !std::is_same_v<const move_only<T>&, Arg >
    >>
   move_only(Arg&& arg, Args&&... args)
      :value(std::forward<Arg>(arg), std::forward<Args>(args)...)
   {}

   move_only(){}
   move_only(const move_only&) = delete;   
   move_only(move_only&& other) noexcept : value(std::move(other.value)) {};    
   ~move_only() noexcept {};   
};

라이브 코드

귀하의 T클래스가 있어야 noexcept이동 생성자 /의 할당은 연산자와 noexcept소멸자. 그렇지 않으면 컴파일 오류가 발생합니다.

std::vector<move_only<MyClass>> vec;

1
복사 생성자를 삭제할 필요는 없습니다. 이동 생성자가 noexcept이면 사용됩니다.
balki

@balki 사용할 수 있습니다. 표준은 지금 이것을 요구하지 않습니다. 다음은 토론 groups.google.com/a/isocpp.org/forum/…입니다.
tower120
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.