본문 바로가기
  • 쓸쓸한 개발자의 공부방
C++

[C++] const reference, return by reference

by 심찬 2021. 8. 14.

 

 

const reference

 

call by value

- 인자로 전달된 객체의 복사본이 생성된다.

  원본 객체를 변경할 수 없기에 안전하지만 복사본에 대한 오버헤드가 있다.

 

<call by value 에 의해 data 객체를 넘기는 경우>

call by reference with const

void foo (const Data& d) 

 

복사본에 대한 오버헤드를 없애고, 안전하게 값을 사용하게 하기 위해서 const reference를 사용할 수 있다.

또한 복사 생성자와 소멸자가 호출되지 않는다.

 

 

#include <iostream>

struct Date
{
    int year;
    int month;
    int day;
};

//void foo(Date d) // call by value 
void foo(const Date& d)      // const reference  : 안전하게 값을 넘겨 사용이 가능하다.
{ 
    // d.year = 1000; // error : const 이기 때문에 값 변경이 불가하다.
}

int main()
{
    Date date = {2020, 3, 28};
    
    foo(date);
    
    std::cout << date.year << std::endl; // 2020
}

 

d.year = 1000; 의 주석을 해제하면 아래와 같은 에러가 발생한다.

main.cpp:13:15: error: assignment of member ‘Date::year’ in read-only object

 

const reference 가 항상 좋은 것은 아니다.

int 혹은 double 타입 값을 받아 처리하는 함수의 경우는 오히려 const reference 가 안 좋다. 이런 경우 call by value를 사용하면 된다.

 - primitive type은 call by value가 좋다.

 - user가 정의한 타입의 경우는 const reference가 좋다.

#include <iostream>

void foo(int x) // call by value
//void foo(const int& x)
{
}

int main()
{
    int n = 10;
    
    // foo 함수에서는 절대로 n의 값을 변경하면 안된다.
    foo(n);
    
    std::cout << n << std::endl; // 반드시 10이 나와야 한다.
}

 

 

 


 

return by reference

 

foo()가 호출되면 pt가 return 될 것처럼 보여지지만 사실 리턴용 임시객체가 생성되어 return 된다.

임시 객체는 등호의 왼쪽에 올 수 없기 때문에 아래와 같은 에러가 발생한다.

 

main() 내부의 foo() 에는 임시 객체가 위치하게 되는 것이다. 그래서 에러 발생!

struct Point
{
    int x;
    int y;
    
    //Point() {}
};

void f1(Point  pt) {} // 복사본 생성
void f2(Point& pt) {} // 복사본 생성 안함.


Point pt = {0,0};

Point foo()  // 값 타입반환  return by value
{
     return pt; 
}    

int main()
{
    foo().x = 10;
    
    // pt.x => 10
}

 

main.cpp:22:15: error: using temporary as lvalue [-fpermissive]

 

아래와 같이 reference 를 사용해 리턴하면 main()에서 foo().x = 10; 수행이 가능해진다.

struct Point
{
    int x;
    int y;
    
    //Point() {}
};

void f1(Point  pt) {} // 복사본 생성
void f2(Point& pt) {} // 복사본 생성 안함.


Point pt = {0,0};

Point& foo()    // 리턴용 임시객체를 만들지 않고 pt를 reference로 return 한다.
{
     return pt; 
}    

int main()
{
    foo().x = 10;
    
    //pt.x => 10
}

 

 

f1()과 같이 함수가 값을 반환하면 :

 - 리턴 용 임시객체가 반환된다.

 - 등호의 왼쪽에 함수 호출식을 놓을 수 없다. 함수 호출식이 lvalue가 될 수 없다.

 

#include <iostream>

int x = 10;

int  f1() { return x;}
int& f2() { return x;}

int main()
{
    f1() = 20; // 10 = 20

    std::cout << x << std::endl; // 20
}

 

 

f2()와 같이 함수가 참조를 반환하면 :

 - 리턴 용 임시객체가 생성되지 않는다.

 - 등호의 왼쪽에 함수 호출식을 놓을 수 있다. 함수 호출식이 lvalue가 될 수 있다.

 

#include <iostream>

int x = 10;

int  f1() { return x;}
int& f2() { return x;}

int main()
{
//    f1() = 20; // 10 = 20
    f2() = 20; // x = 20 ok
    
    std::cout << x << std::endl; // 20
}

 

 

 

* 주의 사항 : reference return 할때, 지역변수를 참조로 반환해서는 안된다!!!

 

f3() 함수가 int&로 reference 형 함수이기 때문에 f3 함수 안의 n는 지역변수로 절대 return 으로 반환해서는 안된다.

(아래는 잘못된 코드이니 따라하지 말자!)

#include <iostream>

int& f3()
{
    int n = 10;   // 지역변수 n
    return n;     // 지역변수는 reference return 으로 반환하면 안된다!!!
}

int main()
{
    std::cout << f3() << std::endl;
}

실행해보면 아래와 같이 warning이 뜨는데, 값이 제대로 출력되지 않는 것은 볼 수 있다.

 

main.cpp:5:9: warning: reference to local variable ‘n’ returned [-Wreturn-local-addr]

 

 

 


revalue reference

 

int&와 같이 reference타입 변수가 선언되었을 때 등호(=)의 오른쪽에 상수는 올 수 없다.

그런데 int&&와 같은 형태의 reference 타입 변수 선언시에는 등호(=) 오른쪽에 상수만 올 수 있다.

 

int main()
{
    int v1 = 0, v2 = 0;
    
    v1 = 10; // ok       10 : rvalue
    10 = v1; // error    v1 : lvalue
    v2 = v1; 
    
    
    // lvalue reference
    int& r1 = v1; // ok 
    int& r2 = 10; // error

    const int& r3 = v1; // ok 
    const int& r4 = 10; // ok
    
    // rvalue reference : rvalue 만 가리킨다.  (C++11부터 지원)
    int&& r5 = v1; // error. 
    int&& r6 = 10; // ok
}

 

 

댓글