RValue Reference (&&)의 반환이 유용한 경우가 있습니까?


79

함수 가 RValue 참조를 반환해야하는 이유가 있습니까? 기술, 트릭, 관용구 또는 패턴?

MyClass&& func( ... );

일반적 으로 참조반환 하는 위험을 알고 있지만 때때로 우리는 어쨌든 그렇게합니다. 그렇지 않습니까? T& T::operator=(T)관용적 인 예입니다. 하지만 T&& func(...)어때요? 그렇게함으로써 얻을 수있는 일반적인 장소가 있습니까? 클라이언트 코드와 비교하여 라이브러리 또는 API 코드를 작성할 때 아마도 다를까요?

답변:


60

적절한 경우가 몇 가지 있지만 비교적 드뭅니다. 클라이언트가 데이터 멤버에서 이동할 수 있도록 허용하려는 경우 한 가지 예가 있습니다. 예를 들면 :

template <class Iter>
class move_iterator
{
private:
    Iter i_;
public:
    ...
    value_type&& operator*() const {return std::move(*i_);}
    ...
};

5
훌륭한 예입니다. 패턴은 당신입니다 원하는 에 클라이언트 코드를 이동 그를 "훔칠"수 - 뭔가. 네, 물론입니다.
towi

4
일반적으로 이동 된 객체는 std :: lib에서 사용될 때 사용중인 std :: lib의 모든 부분에 대해 지정된 모든 요구 사항을 충족해야합니다. std 정의 유형은 이동 된 상태가 유효한지 추가로 보장해야합니다. 클라이언트는 해당 함수 호출에 대한 값에 대한 전제 조건이없는 한 해당 객체로 모든 함수를 호출 할 수 있습니다.
Howard Hinnant 2011

3
마지막으로 위의 예에서 이동 된 개체가 없습니다. std :: move는 움직이지 않습니다. rvalue로만 캐스팅됩니다. 해당 rvalue에서 이동하거나 이동하지 않는 것은 클라이언트의 몫입니다. 해당 클라이언트는 반복기 순회를 개입하지 않고 move_iterator를 두 번 역 참조하는 경우에만 이동 된 값에 액세스합니다.
Howard Hinnant 2011

3
반환 유형 value_type대신 사용하는 것이 더 안전하지 value_type&&않습니까?
fredoverflow

1
네, 그렇게 생각합니다. 그러나이 경우 추가 된 이익이 위험보다 크다고 생각합니다. move_iterator는 복사 알고리즘을 움직이는 알고리즘으로 바꾸기 위해 일반 코드에서 자주 사용됩니다 (예 : vector :: insert의 이동 버전). 그런 다음 값 비싼 사본이있는 유형을 제공하고 일반 코드로 이동하면 추가 사본이 무상으로 추가됩니다. 예를 들어 array <int, N>을 생각하고 있습니다. 이들을 벡터에 이동 삽입 할 때 실수로 추가 사본을 도입하고 싶지는 않습니다. 위험 측면에서는 const X& x = *i매우 드뭅니다. 나는 그것을 본 적이 없다고 생각한다.
Howard Hinnant

17

이것은 towi의 의견에 이어집니다. 지역 변수에 대한 참조를 반환하고 싶지 않습니다. 그러나 당신은 이것을 가질 수 있습니다.

vector<N> operator+(const vector<N>& x1, const vector<N>& x2) { vector<N> x3 = x1; x3 += x2; return x3; }
vector<N>&& operator+(const vector<N>& x1, vector<N>&& x2)    { x2 += x1; return std::move(x2); }
vector<N>&& operator+(vector<N>&& x1, const vector<N>& x2)    { x1 += x2; return std::move(x1); }
vector<N>&& operator+(vector<N>&& x1, vector<N>&& x2)         { x1 += x2; return std::move(x1); }

두 매개 변수가 모두 lvalue 인 경우를 제외하고 모든 경우에 복사 (및 가능한 할당)를 방지해야합니다.


1
가능하지만이 접근 방식에는 임시 저장 외에 자체 문제가 있기 때문에 일반적으로 눈살을 찌푸립니다. 참조 stackoverflow.com/questions/6006527
sellibitze

2
이러한 반환 호출은 std :: move ()를 사용할 필요가 없습니까?
wjl

1
@wjl : 좋은 질문이지만 그렇게 생각하지 않습니다. std :: move는 std :: move를 사용하지 않고 작동합니다. &&에 대한 캐스트가 여기서 트릭을 수행한다고 생각합니다.
클린턴

1
@Clinton 코드에 캐스트가 없습니다 return std::move(x2);. 또는 캐스트를 rvalue 참조 유형으로 작성할 수 move있습니다.
MM

1
코드는 반환 값이 사용되지 않거나 객체에 할당 된 경우에만 정확하지만 값으로 반환하고 값으로 인수를 가져와 복사 제거가 그 일을 수행하도록 할 수도 있습니다.
MM

7

아니요. 값만 반환합니다. 일반적으로 참조를 반환하는 것은 전혀 위험하지 않습니다 . 위험한 지역 변수에 대한 참조를 반환 하는 것입니다. 그러나 rvalue 참조를 반환하는 것은 거의 모든 상황에서 꽤 쓸모가 없습니다 (당신이 글을 std::move쓰거나 무언가 를 쓰고 있었다고 생각합니다 ).


5
나는 C ++ 0x의 초기 디자인 중에 이동 할당 과 같은 것을 제안 T&& operator+(const T&,T&&)하고 &&. 하지만 이제 최종 초안에서는 사라졌습니다. 그것이 내가 묻는 이유입니다.
towi 2011-04-24

1

함수가 종료 된 후 참조 된 개체가 범위를 벗어나지 않을 것이라고 확신하는 경우 참조로 반환 할 수 있습니다 (예 : 전역 개체의 참조 또는 클래스 필드에 대한 참조를 반환하는 멤버 함수 등).

이 반환 참조 규칙은 lvalue 및 rvalue 참조와 동일합니다. 차이점은 반환 된 참조를 사용하는 방법입니다. 내가 볼 수 있듯이 rvalue 참조로 반환하는 것은 드뭅니다. 기능이있는 경우 :

Type&& func();

이러한 코드가 마음에 들지 않습니다.

Type&& ref_a = func();

명명 된 rvalue 참조가 lvalue이기 때문에 ref_a를 Type &로 효과적으로 정의하기 때문에 여기에서는 실제 이동이 수행되지 않습니다. 다음과 같습니다.

const Type& ref_a = func();

실제 ref_a가 상수가 아닌 lvalue 참조라는 점을 제외하고.

또한 Type && 인수를받는 다른 함수에 func ()를 직접 전달하는 경우에도 그 함수 내에서 명명 된 참조이므로 유용하지 않습니다.

void anotherFunc(Type&& t) {
  // t is a named reference
}
anotherFunc(func());

func () 및 anotherFunc ()의 관계는 func ()가 anotherFunc ()가 func ()에서 반환 된 객체의 소유권을 가질 수 있다는 데 동의하는 "권한 부여"와 비슷합니다. 그러나이 합의는 매우 느슨합니다. 상수가 아닌 lvalue 참조는 호출자가 여전히 "도용"할 수 있습니다. 실제로 함수는 rvalue 참조 인수를 사용하도록 정의되지 않습니다. 가장 일반적인 경우는 "anotherFunc"가 클래스 이름이고 anotherFunc ()는 실제로 이동 생성자입니다.


0

한 가지 더 가능한 경우 : 튜플을 풀고 값을 함수에 전달해야하는 경우입니다.

이 경우 복사 제거에 대해 확실하지 않은 경우 유용 할 수 있습니다.

이러한 예 :

template<typename ... Args>
class store_args{
    public:
        std::tuple<Args...> args;

        template<typename Functor, size_t ... Indices>
        decltype(auto) apply_helper(Functor &&f, std::integer_sequence<size_t, Indices...>&&){
            return std::move(f(std::forward<Args>(std::get<Indices>(args))...));
        }

        template<typename Functor>
        auto apply(Functor &&f){
            return apply_helper(std::move(f), std::make_index_sequence<sizeof...(Args)>{});
        }
};

당신이 어떤 형태의 std::bind또는 std::thread대체를 작성하지 않는 한 매우 드문 경우 입니다.

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