`const` 객체에`std :: move`를 사용할 수있는 이유는 무엇입니까? //but because

C ++ 11에서는 다음 코드를 작성할 수 있습니다.

struct Cat {
   Cat(){}
};

const Cat cat;
std::move(cat); //this is valid in C++11

를 호출 std::move하면 개체를 이동하고 싶다는 의미입니다. 즉, 개체를 변경하겠습니다. const개체 를 이동하는 것은 비합리적인데 왜이 std::move동작을 제한 하지 않습니까? 장래에 함정이 되겠죠?

여기에 트랩은 Brandon이 주석에서 언급했듯이 의미합니다.

“내 생각에 그가 몰래 은밀하게”덫을 놓는다 “는 뜻이라고 생각한다. 왜냐하면 그가 깨닫지 못하면 의도 한 것과 다른 사본으로 끝나기 때문이다.”

Scott Meyers의 ‘Effective Modern C ++’책에서 그는 다음과 같은 예를 제공합니다.

class Annotation {
public:
    explicit Annotation(const std::string text)
     : value(std::move(text)) //here we want to call string(string&&),
                              //but because text is const, 
                              //the return type of std::move(text) is const std::string&&
                              //so we actually called string(const string&)
                              //it is a bug which is very hard to find out
private:
    std::string value;
};

물체 std::move에 대한 작동이 금지되어 있다면 const버그를 쉽게 찾을 수 있었죠?



답변

struct strange {
  mutable size_t count = 0;
  strange( strange const&& o ):count(o.count) { o.count = 0; }
};

const strange s;
strange s2 = std::move(s);

여기서 우리 std::moveT const. 그것은을 반환합니다 T const&&. strange정확히이 유형을 취하는 이동 생성자가 있습니다.

그리고 그것은 불린다.

이제이 이상한 유형이 제안이 수정할 버그보다 더 드물다는 것은 사실입니다.

그러나, 다른 한편으로는, 기존의 std::move사용자가 작업중인 유형이 있는지 알고하지 않는 일반적인 코드의 작품 더 나은 T또는를 T const.


답변

여기에 간과하는 트릭이 있습니다. 즉, std::move(cat) 실제로 아무것도 움직이지 않는 것 입니다. 단지 컴파일러 에게 이동 을 시도 하도록 지시합니다 . 그러나 클래스에를 허용하는 생성자가 없으므로 const CAT&&대신 암시 적 const CAT&복사 생성자를 사용하고 안전하게 복사합니다. 위험도, 함정도 없습니다. 어떤 이유로 든 복사 생성자가 비활성화 된 경우 컴파일러 오류가 발생합니다.

struct CAT
{
   CAT(){}
   CAT(const CAT&) {std::cout << "COPY";}
   CAT(CAT&&) {std::cout << "MOVE";}
};

int main() {
    const CAT cat;
    CAT cat2 = std::move(cat);
}

인쇄 COPY가 아니라 MOVE.

http://coliru.stacked-crooked.com/a/0dff72133dbf9d1f

언급 한 코드의 버그 는 안정성 문제 가 아니라 성능 문제 이므로 이러한 버그로 인해 충돌이 발생하지 않습니다. 더 느린 복사본을 사용합니다. 또한 이러한 버그는 이동 생성자가없는 상수가 아닌 개체에 대해서도 발생하므로 단순히 오버로드를 추가한다고해서 모든 개체가 포착되지는 않습니다. 매개 변수 유형에서 구성을 이동하거나 할당을 이동하는 기능을 확인할 수 있지만 복사 생성자에서 폴백 해야하는 일반 템플릿 코드를 방해 합니다. 그리고 도대체 누군가가에서 건설 할 수 있기를 원할 수도 있습니다. 그가 할 수 없다고 말하는 사람은 누구입니까?constconst CAT&&


답변

지금까지 나머지 답변이 간과 된 한 가지 이유는 일반 코드가 이동시 탄력적 일 수 있기 때문입니다 . 예를 들어 동일한 값을 가진 다른 종류의 컨테이너를 만들기 위해 한 종류의 컨테이너에서 모든 요소를 ​​이동하는 일반 함수를 작성하고 싶다고 가정 해 보겠습니다.

template <class C1, class C2>
C1
move_each(C2&& c2)
{
    return C1(std::make_move_iterator(c2.begin()),
              std::make_move_iterator(c2.end()));
}

멋지다, 이제는 상대적으로 효율적으로 a vector<string>를 만들 수 deque<string>있고 각 개인 string이 그 과정에서 움직일 것입니다.

하지만에서 이동하려면 어떻게해야 map합니까?

int
main()
{
    std::map<int, std::string> m{{1, "one"}, {2, "two"}, {3, "three"}};
    auto v = move_each<std::vector<std::pair<int, std::string>>>(m);
    for (auto const& p : v)
        std::cout << "{" << p.first << ", " << p.second << "} ";
    std::cout << '\n';
}

경우 std::move비 주장 const인수의 위의 인스턴스는 move_each이동하려고하기 때문에 컴파일되지 않을 것이라고 const int합니다 ( key_typemap). 하지만이 코드는 상관하지 않는다 는 이동할 수없는 경우 key_type. 성능상의 이유로 mapped_type( std::string) 를 이동하려고합니다 .

그것은이 예를 들어, 그리고 일반 그 코딩의 그것과 같은 수많은 예를 들면 std::moveA는 이동 요청 이 아니라 이동하는 수요.


답변

나는 OP와 같은 관심사를 가지고 있습니다.

std :: move는 개체를 이동하지 않으며 개체가 이동 가능함을 보장하지도 않습니다. 그럼 왜 무브라고 부르나요?

움직일 수없는 것은 다음 두 가지 시나리오 중 하나 일 수 있다고 생각합니다.

1. 이동 유형은 const입니다.

언어에 const 키워드가있는 이유는 컴파일러가 const로 정의 된 객체에 대한 변경을 방지하기를 원하기 때문입니다. Scott Meyers의 책에서 예를 들면 다음과 같습니다.

    class Annotation {
    public:
     explicit Annotation(const std::string text)
     : value(std::move(text)) // "move" text into value; this code
     {  } // doesn't do what it seems to!    
     
    private:
     std::string value;
    };

문자 그대로 무엇을 의미합니까? const 문자열을 값 멤버로 이동하십시오. 적어도 설명을 읽기 전에 이해합니다.

언어가 이동을하지 않거나 std :: move ()가 호출 될 때 이동이 적용 가능하다는 것을 보장하지 않으려는 경우 단어 이동을 사용할 때 문자 그대로 오해의 소지가 있습니다.

언어가 std :: move를 사용하는 사람들이 더 나은 효율성을 갖도록 장려한다면, 특히 이런 유형의 명백한 문자 모순에 대해 가능한 한 빨리 이와 같은 함정을 방지해야합니다.

나는 사람들이 상수 이동이 불가능하다는 것을 알고 있어야한다는 데 동의하지만,이 의무는 명백한 모순이 발생할 때 컴파일러가 침묵 할 수 있음을 의미해서는 안됩니다.

2. 객체에 이동 생성자가 없습니다.

개인적으로 이것은 Chris Drew가 말했듯이 OP의 우려와는 별개의 이야기라고 생각합니다.

@hvd 그것은 나에게 약간의 논증처럼 보입니다. OP의 제안이 세상의 모든 버그를 수정하지 않는다고해서 반드시 그것이 나쁜 생각이라는 것을 의미하지는 않습니다 (아마도 그럴 것이지만 당신이주는 이유 때문은 아닙니다). – 크리스 드류


답변

아무도 이것의 이전 버전과의 호환성 측면을 언급하지 않은 것에 놀랐습니다. 저는 std::move의도적으로 C ++ 11에서이를 수행하도록 설계 되었다고 생각 합니다. C ++ 98 라이브러리에 크게 의존하는 레거시 코드베이스로 작업하고 있으므로 복사 할당에 대한 폴백없이 이동하면 문제가 발생한다고 상상해보십시오.