[c++] std::move std::forward 차이 사용

TNT·2025년 5월 10일
0

c++ 기초

목록 보기
15/17

이번 포스트는 c++11 에 생긴 std::move std::forward 두 함수의 차이랑 사용 용도를 포스트 하겠습니다.

이 두 함수를 알려면 먼저 lvalue와 rvalue에 대한 이해를 이해 해야하는데 여기서는 간단하게 설명을 하고 나중에 따로 포스트 하겠습니다.

lvalue: 이름이 있는 메모리 주소를 가질 수 있는 표현식입니다 대입문의 왼쪽에 올 수 있으며 여러 번 참조될 수 있습니다.

// 예시
int x;
std::string s;

rvalue: 임시적인 값 또는 이름이 없는 표현식입니다 대입문의 오른쪽에만 올 수 있으며 보통 한 번 사용되고 사라집니다.

10;
std::string("hello");
x + y;

간단하게 요약을 하자면 변수 선언 할때
(lvalue) int x = (rvalue) 10;
왼쪽이 lvalue 오른쪽이 rvalue이다.

1.std::move

std::move부터 설명 하자면 인자로 전달 하는 객체는 무조건 rvalue 참조 타입으로 캐스팅 합니다.(xvalue)
조금 이해를 위해서 풀어서 설명 하자면 객체를 복사 하지 않고 데이터를 넘깁니다.

내부 코드롤 보게 된다면

template<typename T>
typename std::remove_reference<T>::type&& move(T&& arg) noexcept {
    return static_cast<typename std::remove_reference<T>::type&&>(arg);
}

으로 구현된걸 볼수있다. 내부 적으로 static_cast를 사용해서 인자를 rvalue참조로 변환후 리턴 해준다.

사용법

보통적으로 멤버 변수들을 이동 시켜야할때 사용한다.

    std::string a = "testdata";
    std::string b = "";
    std::string c = "";

    b = std::move(a);
    c = std::move(b);

    std::cout << "a =" << a << std::endl;
    std::cout << "b =" << b << std::endl;
    std::cout << "c =" << c << std::endl;

실행 결과

결과를 보게 된다면 b로 가져가고 c로 가져가서 c에 데이터가 있는 상태 이다.
어떤 함수에 보내서 더이상 데이터를 안쓰거나 하게 될경우 많이 사용하고있다.

2. std::forward

std::forward는 인자의 값의 종류를 보존하면서 다른 함수로 전달 할때 사용합니다.

사용하는 이유는

템블릿 함수에서 인자를 받을때 T&&를 많이 사용하는데
이 T&&는 전달되는 인자가 lvalue면 T 는 lvalue 참조 타입으로 추론되고 ravlue면 T는 비참조 타입으로 추론됩니다.
들어오는 타입에 따라서 변경이 되는것이 많습니다.

함수 템플릿 내에서는 T&&로 선언도니 매개변수 자체는 변수명이 있습니다. 그러면 lvalue 타입이라는걸 알수있다.

사용법

결국엔 특정 조건에서만 바꿔서 전달 하고 아닌경우에는 그냥 그대로 리턴 하는것이다.
조금 복잡하기에 잘 갈무리된 모두의 코드 링크 걸어두고 코드를 가지고왔다.

#include <iostream>
#include <memory>
#include <utility>

struct A {
  A(int&& n) { std::cout << "rvalue overload, n=" << n << "\n"; }
  A(int& n) { std::cout << "lvalue overload, n=" << n << "\n"; }
};

class B {
 public:
  template <class T1, class T2, class T3>
  B(T1&& t1, T2&& t2, T3&& t3)
      : a1_{std::forward<T1>(t1)},
        a2_{std::forward<T2>(t2)},
        a3_{std::forward<T3>(t3)} {}

 private:
  A a1_, a2_, a3_;
};

template <class T, class U>
std::unique_ptr<T> make_unique1(U&& u) {
  return std::unique_ptr<T>(new T(std::forward<U>(u)));
}

template <class T, class... U>
std::unique_ptr<T> make_unique2(U&&... u) {
  return std::unique_ptr<T>(new T(std::forward<U>(u)...));
}

int main() {
  auto p1 = make_unique1<A>(2);  // rvalue
  int i = 1;
  auto p2 = make_unique1<A>(i);  // lvalue

  std::cout << "B\n";
  auto t = make_unique2<B>(2, i, 3);
}

모두의 코드

실행 결과

결과를 전체적으로 비교 하자면

std::move: 무조건 rvalue로 만듦
std::forward: 원래 lvalue였으면 lvalue로, rvalue였으면 rvalue로 전달
한다고 이해 하고 있으면 될꺼같다.

profile
개발

0개의 댓글